繁体   English   中英

名单 <T> .ForEach与自定义IEnumerable <T> 延期

[英]List<T>.ForEach vs. custom IEnumerable<T> extension

说我有一节课:

public class MyClass
{
   ...
}

和一个返回IEnumerable<MyClass>的webservice方法

Web服务的使用者定义了一些方法:

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

现在,消费者可以通过两种方式在Web服务方法的结果上调用DoSomething

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方法呢?

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

您可以编写扩展方法,但有充分的理由说明为什么ForEach没有在IEnumerable<T> 第二个例子

result.ToList().ForEach(DoSomething);

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

附录:

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

第二个原因是,这样做会增加语言的新代表性能力。 这样做可以让你重写这个完全清晰的代码:

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

进入这段代码:

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

它使用几乎完全相同的字符,顺序略有不同。 然而第二个版本更难理解,更难调试,并引入了闭包语义,从而可能以微妙的方式改变对象的生命周期。

我更喜欢这个: -

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

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

我用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;
    }

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM