代码之家  ›  专栏  ›  技术社区  ›  Klaus Byskov Pedersen

list<t>.foreach与custom ienumerable<t>扩展

  •  4
  • Klaus Byskov Pedersen  · 技术社区  · 16 年前

    假设我有课:

    public class MyClass
    {
       ...
    }
    

    以及返回 IEnumerable<MyClass>

    webservice的使用者定义了一些方法:

    public void DoSomething(MyClass myClass)
    {
       ...
    }
    

    现在,消费者可以打电话 DoSomething 关于webservice方法的两种结果:

    var result = // web service call
    
    foreach(var myClass in result)
    {
       DoSomething(myClass);
    }
    

    或:

    var result = // web service call
    
    result.ToList().ForEach(DoSomething);
    

    不用说,我更喜欢第二种方式,因为它要短得多,表达能力更强(一旦你习惯了语法,我已经习惯了)。

    现在,web服务方法只公开 IEnumerable<MyClass> ,但它实际上返回一个 List<MyClass> 这(afaik)意味着实际的序列化对象仍然是 List<T> . 然而,我发现(使用反射器)linq方法 ToList() 复制中的所有对象 IEnumerable<T> 不管实际的运行时类型如何(在我看来,它可能只是将参数强制转换为 列表<t> 如果它已经是一个)。

    这显然有一些性能开销,特别是对于大列表(或大对象列表)。

    所以我能做些什么来克服这个问题,为什么没有 ForEach 林肯的方法?

    顺便说一下,他的问题和 this one

    6 回复  |  直到 16 年前
        1
  •  4
  •   TheSoftwareJedi jac    16 年前

    我用两种方法。一个迭代列表,一个使用lazy eval。我用它们作为情况的定义。

        public static IEnumerable<T> ForEachChained<T>(this IEnumerable<T> source, Action<T> action)
        {
            foreach (var item in source)
            {
                action(item);
                yield return item;
            }
        }
    
        public static IEnumerable<T> ForEachImmediate<T>(this IEnumerable<T> source, Action<T> action)
        {
            foreach (var item in source)
            {
                action(item);
            }
            return source;
        }
    
        2
  •  5
  •   Jamie Ide    16 年前

    你可以写一个扩展方法,但是 good reasons 为什么没有在上实现foreach IEnumerable<T> . 第二个例子

    result.ToList().ForEach(DoSomething);
    

    将IEnumerable复制到一个列表中(除非它已经是一个列表,我假设),所以您最好将IEnumerable与good old进行迭代 foreach(var r in result) {} .

    附录:

    对我来说,eric lippert这篇文章的重点是添加foreach没有任何好处,并增加了一些潜在的陷阱:

    第二个原因是这样做 添加零个新的表示力 对语言。这样做可以让你 重写这段非常清晰的代码:

    foreach(foo foo in foos){语句 涉及foo;}

    输入此代码:

    foos.foreach((foo foo)=>{语句 涉及foo;});

    使用几乎完全相同的 稍有不同的人物 秩序。但第二个版本是 更难理解,更难调试, 引入闭包语义, 从而可能改变对象 以微妙的方式生活。

        3
  •  5
  •   AnthonyWJones    16 年前

    我更喜欢这个:

    foreach (var item in result.ToList())
    {
       DoSomething(item);
    }
    

    这是一个更清楚的成语,它说收集一系列的东西在一起 然后 做一些可能改变应用程序状态的重要事情。它的老派,但它的工作,实际上是更容易理解的更广泛的观众。

        4
  •  2
  •   Klaus Byskov Pedersen    16 年前

    您可以编写自己的扩展方法 IEnumerable<T> 像这样:

        public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
        {
            foreach (T t in enumerable)
                action(t);
        } 
    

    这种方法在linq中不存在,因为linq基本上用于查询,而不是简单的迭代。

    另外,请注意,当使用 List<T> 实例, 不会调用扩展方法,因为实例方法在共享签名时优先于扩展方法。

    例如,以下代码不会调用扩展方法:

     var l = new List<MyClass>();
     l.Add(new MyClass());
     l.ForEach(DoSomething);
    

    鉴于以下情况:

    IEnumerable<MyClass> l = new List<MyClass>(new []{new MyClass()});
    l.ForEach(DoSomething);  
    
        5
  •  1
  •   AndreasKnudsen    16 年前

    您可以编写自己的扩展方法tolist(this list thelist){returnthelist;},然后避免开销。因为您的扩展方法是最具体的方法,所以它将被调用,而不是IEnumerable上的方法

        6
  •  -2
  •   CodingInsomnia    16 年前

    如果您决定通过一个扩展方法来实现这一点,我将调用它forall而不是foreach。这是为了使用与 Parallel Extensions in .NET 4.0 用途:

    var result = // web service call
    result.AsParallel().ForAll(DoSomething);