列出<t> .ForEach与自定义IEnumerable <t>扩展名</t> </t>

时间:2010-02-19 15:22:11

标签: c# linq extension-methods

说我有课:

public class MyClass
{
   ...
}

以及返回IEnumerable<MyClass>

的webservice方法

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>中所有对象的副本,而不管实际的运行时类型(在我看来,它可能只是已经演绎了参数到List<T>,如果它已经是一个)。

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

那么我该怎么做才能解决这个问题,为什么Linq中没有ForEach方法?

顺便说一句,他的问题与this one含糊不清。

6 个答案:

答案 0 :(得分:5)

您可以编写扩展方法,但有good reasons为什么IEnumerable<T>上未实现ForEach。第二个例子

result.ToList().ForEach(DoSomething);

将IEnumerable复制到List中(除非它已经是List,我假设)所以你最好只用旧的foreach(var r in result) {}迭代IEnumerable。

附录:

对我而言,Eric Lippert的文章的关键点是添加ForEach没有任何好处,并增加了一些潜在的陷阱:

  

第二个原因就是这样做   增加零代表性能力   对语言。这样做可以让你   重写这个非常清晰的代码:

     

foreach(foo foo in foos){声明   涉及foo; }

     

进入此代码:

     

foos.ForEach((Foo foo)=&gt; {声明   涉及foo; });

     

使用几乎完全相同   字符略有不同   订购。然而第二个版本是   更难理解,更难调试,   并介绍了闭包语义,   从而可能改变对象   生活中的微妙方式。

答案 1 :(得分:5)

我更喜欢这个: -

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

它是一个更清晰的习语,它说收集一个列表的东西然后做一些重要的事情可能会改变应用程序的状态。它的旧学校虽然有效但实际上对更广泛的受众更容易理解。

答案 2 :(得分:4)

我使用2种方法。一个迭代列表,一个使用懒惰的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;
    }

答案 3 :(得分:2)

您可以为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);  

答案 4 :(得分:1)

您可以编写自己的扩展方法ToList(此List theList){return theList;}然后避免开销。由于您的扩展方法是最具体的方法,因此将调用它,而不是IEnumerable

上的方法

答案 5 :(得分:-2)

如果您决定通过扩展方法执行此操作,我将其称为ForAll而不是ForEach。这是为了使用与Parallel Extensions in .NET 4.0使用相同的语法:

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