带有条件的列表方法链式调用

4
var someList = otherList.ReplaceAll(foo, bar);

if (someCondition)
{
    someList = someList.ReplaceAll(someOtherFoo, someOtherBar);
}

在我的代码中,我有以上代码片段,我发现if语句非常令人烦恼,希望改为以下方式:

var someList = otherList
    .ReplaceAll(foo, bar)
    .Given(someCondition)
    .ReplaceAll(someOtherFoo, someOtherBar);

这是否有可能实现:ReplaceAll(someOtherFoo,someOtherBar)仅在someConditiontrue时执行?


1
这个问题中没有LINQ运算符。只需使用方法链、使用您自己的方法或某些第三方库。ReplaceAll是什么意思,它与LINQ有什么关系? - Panagiotis Kanavos
只有当.Given方法在List上设置或重置某种标志并返回List,然后.ReplaceAll“神奇地”知道该标志并理解如果已设置或未设置该标志,则不应执行任何操作时,您提出的想法才有可能实现。如果这一切都是关于标准的List<T>,那么坏消息是它不包含可以设置的此类标志。 - Peter B
如果您不想在枚举对象上使用标志,可以创建一个环境上下文来保存这些信息...并不是说这样做会产生易于编写/良好的代码或者没有代价...但如果坚持使用标准的List<T>是问题所在的话... - DarkSquirrel42
@PanagiotisKanavos 我想我应该重新表达我的问题,感谢您的建议。 - Noobnewbier
4个回答

4
虽然像其他人建议的那样创建扩展是可行的,但在实际的生产代码中我不会这样做,因为这样做违背了Linq的目的。
Linq是功能性的,很适合处理序列或流。每个Linq链接运算符都会处理传入的数据并将其转换为另一个数据。您要查找的Given扩展是过程性的,并且不对序列执行任何操作。
同时,Given也不支持Linq的惰性评估特性之一。
通过引入这样的扩展,您只是让下一个在该代码上工作的人更难阅读代码。
相比之下,老式的if语句可以被每个人轻松理解。
如果您想节省几行代码,可以使用三元运算符:
var someList = otherList.ReplaceAll(foo, bar);
someList = someCondition ? someList.ReplaceAll(someOtherFoo, someOtherBar) : someList;

我肯定会使用简单的 if 语句。如果 (条件) someList = someList.ReplaceAll(....); - Daniel Schmid
谢谢您的建议,尽管它并没有严格回答我的问题! - Noobnewbier
@Noobnewbier 谢谢!有时候对于“我该如何做X”的问题,最好的答案是“你不需要它,用Y代替即可”。C#当然允许编写像count.While(condition, i => i += 1)这样的代码,但那只是一种奇怪的表达方式,等同于while(condition()) { count += 1; } - Maxim Kosov

1
为什么不创建一个扩展程序?

    public static List<T> ExecuteIf<T>(
        this List<T> list, 
        Func<bool> condition, 
        Func<List<T>, List<T>> action)
    {
        return condition() ? action(list) : list;
    }



var someList = otherList
    .ReplaceAll(foo, bar)
    .ExecuteIf(() => someCondition, (l) => l.ReplaceAll(someOtherFoo, someOtherBar));


1
为了使用流畅的方法链与这样的条件操作,您可以创建一个名为ReplaceAllIf的扩展方法:
public static class Extensions
{
    public static List<T> ReplaceAllIf<T>(this List<T> list, bool condition, T valueToFind, T replacement)
    {
        return condition ? list.ReplaceAll(valueToFind, replacement) : list;
    }

    public static List<T> ReplaceAll<T>(this List<T> list, T valueToFind, T replacement)
    {
        return list.Select(x => x.Equals(valueToFind) ? replacement : x).ToList();
    }
}

然后像这样使用它:

var list = new List<string>{"a", "b", "c", "d", "a", "b", "c", "d"};

var result = list
    .ReplaceAll("a", "XXXX")
    .ReplaceAllIf(true, "c", "YYYY")
    .ReplaceAllIf(false, "d", "ZZZZ");

工作演示 -> https://dotnetfiddle.net/gknS4z


我正在尝试寻找更通用的解决方案,如果可能的话。你所建议的方法可以解决这个具体的使用情况,但是如果我想要做其他事情,我也需要为它们编写不同的方法。 - Noobnewbier

1
你所描述的被称为流畅接口。
LINQ 函数是具有以下签名的扩展函数。
IEnumerable<T> someFunction<T>(this IEnumerable<T>, ...)

如您所见,这是一个普通的函数,返回某些内容...

流畅接口通过返回实现枚举接口的内容来使用它,在这种情况下,还使用返回类型来改变您可以在结果上调用的函数集合...

以下是一个示例程序...

using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace SoFluentExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var en = Enumerable.Range(0, 10).Concat(Enumerable.Range(0, 10));

            var result = en
                            .ReplaceAll(1, 100)
                            .Given(true)
                            .ReplaceAll(2, 200)
                            .Given(false)
                            .ReplaceAll(3,300)
                            .ToArray();
        }
    }

    public class MyFluentEnumerableWithCondition<T> : IEnumerable<T>
    {
        public IEnumerable<T> en { get; private set; }
        public bool condition { get; private set; }

        public MyFluentEnumerableWithCondition(IEnumerable<T> en, bool condition)
        {
            this.en = en;
            this.condition = condition;
        }
        public IEnumerator<T> GetEnumerator()
        {
            return en.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return en.GetEnumerator();
        }
    }
    public class MyFluentReplacerEnumerable<T> : IEnumerable<T> 
    {
        private IEnumerable<T> en;
        private T foo;
        private T bar;
        public MyFluentReplacerEnumerable(IEnumerable<T> en, T foo, T bar)
        {
            this.en = en;
            this.foo = foo;
            this.bar = bar;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return new MyEnumerator(en.GetEnumerator(), foo, bar);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        private class MyEnumerator : IEnumerator<T>
        {
            private IEnumerator<T> en;
            private T foo;
            private T bar;
            public MyEnumerator(IEnumerator<T> enumerator,T foo, T bar)
            {
                this.en = enumerator;
                this.foo = foo;
                this.bar = bar;
            }
            public T Current => replace(en.Current,foo,bar);

            private T replace(T current, T foo, T bar)
            {
                return current.Equals(foo) ? bar : current;
            }

            object IEnumerator.Current => Current;

            public bool MoveNext()
            {
                return en.MoveNext();
            }

            public void Reset()
            {
                en.Reset();
            }

            #region IDisposable Support
            private bool disposedValue = false; 

            protected virtual void Dispose(bool disposing)
            {
                if (!disposedValue)
                {
                    if (disposing)
                    {
                        en.Dispose();
                    }
                    disposedValue = true;
                }
            }
            public void Dispose()
            {
                Dispose(true);
            }
            #endregion
        }
    }
    public static class MyExtension
    {
        public static IEnumerable<T> ReplaceAll<T>(this IEnumerable<T> en,T foo, T bar)
        {
            return new MyFluentReplacerEnumerable<T>(en, foo, bar);
        }

        public static MyFluentEnumerableWithCondition<T> ReplaceAll<T>(this MyFluentEnumerableWithCondition<T> en, T foo, T bar)
        {
            if (!en.condition)
                return en;
            return new MyFluentEnumerableWithCondition<T>(en.en.ReplaceAll(foo,bar),true);
        }
        public static MyFluentEnumerableWithCondition<T> Given<T>(this IEnumerable<T> en, bool condition)
        {
            return new MyFluentEnumerableWithCondition<T>(en, condition);
        }
    }

}

ReplaceAll 被定义在 IEnumerable<T>MyFluentEnumerableWithCondition<T> 中,具体实现由类型决定,即使 MyFluentEnumerableWithCondition<T> 实现了 IEnumerable<T>,更具体的签名仍然被使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接