2

我正在考虑替换如下代码:

        foreach (var meshCut in meshCuts0)
        {
            ComputePolygons(meshCut, polygons);
        }
        foreach (var meshCut in meshCuts1)
        {
            ComputePolygons(meshCut, polygons);
        }

linq 看起来像这样:

        meshCuts0.Concat(meshCuts1).ForEach(m => ComputePolygons(m, polygons));

我不知道 linq 是如何实现的,所以我不确定性能后果。我会很感激一些帮助。

1)

Concat 将创建一个列表副本还是只是一个枚举器执行以下操作:

    public static IEnumerable<T> Concat<T>(IEnumerable<T> a, IEnumerable<T> b)
    {
        foreach (var t in a)
        {
            yield return t;
        }
        foreach (var t in b)
        {
            yield return t;
        }
    }

2)

这会是 Mono 上的相同行为吗?

3)

是否有任何参考解释如何为性能意识实现 linq api 函数?

谢谢!

编辑

好的,没有 ForEach 所以假设我也定义了这样的东西:

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

真正的问题是,Concat 是否会因为精简代码而成为代价高昂的开销,多亏了评论,我现在明白事实并非如此。

编辑 2

啊,Jon Skeet 建议不要添加 ForEach ......所以我不会!

http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx

4

3 回答 3

10

首先,只有. 我建议您不要那样做 - 请参阅Eric Lippert 的博客文章,原因我同意。ForEachIEnumerable<T>

我建议你把它写成:

foreach (var meshCut in meshCuts0.Concat(meshCuts1))
{
    ComputePolygons(meshCut, polygons);
}

Concat执行良好 - 它只是迭代第一个序列,然后是第二个序列,生成项目。它不会缓冲所有元素。您添加的额外间接级别会带来非常轻微的性能损失,但仅此而已。

我希望 Mono 的行为方式相同 -非常Concat简单。事实上,由于 Mono 是开源的,你可以自己检查一下。(当然,它可能会随着时间的推移而移动......)

不久前,我在博客中详细介绍了 LINQ to Objects,从头开始重新实现整个事物,并记录了其性能的各个方面,包括有改进空间的地方。有关更多详细信息,请参阅我的Edulinq 博客系列

于 2013-07-13T22:50:40.330 回答
4

这就是 linq concat 在幕后工作的方式。

微软 .net 框架 4.0:

public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) {
    if (first == null) throw Error.ArgumentNull("first");
    if (second == null) throw Error.ArgumentNull("second");
    return ConcatIterator<TSource>(first, second); 
}

static IEnumerable<TSource> ConcatIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second) { 
    foreach (TSource element in first) yield return element;
    foreach (TSource element in second) yield return element; 
}

单声道(来源:https ://github.com/mono/mono/blob/master/mcs/class/System.Core/System.Linq/Enumerable.cs#L584 )

public static IEnumerable<TSource> Concat<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second)
{
    Check.FirstAndSecond (first, second);

    return CreateConcatIterator (first, second);
}

static IEnumerable<TSource> CreateConcatIterator<TSource> (IEnumerable<TSource> first, IEnumerable<TSource> second)
{
    foreach (TSource element in first)
        yield return element;
    foreach (TSource element in second)
        yield return element;
}

具体答案:

  1. 它不是创建任何副本,它只是枚举。

  2. 是的。

  3. 对于性能意识,源代码是最好的。

于 2013-07-13T22:44:52.230 回答
1
  1. 它就像你描述的那样工作。您可以下载名为JetBrains dotPeek - .net 反编译器的免费工具。您可以将每个程序集,甚至标准程序集加载到反编译器并获取源代码。如果您查看程序集 System.Core、命名空间 System.Linq、类 Enumerable,您可以看到:

    public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) {
        if (first == null) throw Error.ArgumentNull("first");
        if (second == null) throw Error.ArgumentNull("second");
        return ConcatIterator<TSource>(first, second); 
    }
    
    static IEnumerable<TSource> ConcatIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second) { 
        foreach (TSource element in first) yield return element;
        foreach (TSource element in second) yield return element; 
    }
    

    谈论 LINQ - 它很懒惰,每个 linq 扩展方法都使用延迟计算。有好人 Jon Skeet,他有一个博客。有许多名为“重新实现 LINQ to Objects: Part”的文章,所有文章都可以通过标签 'linq' 找到:link

  2. Mono 是开源项目。您可以在github上找到源代码。类 Enumerable 可以在这里建立:链接。以及Concat的源代码:

    public static IEnumerable<TSource> Concat<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second)
    {
        Check.FirstAndSecond (first, second);
    
        return CreateConcatIterator (first, second);
    }
    
    static IEnumerable<TSource> CreateConcatIterator<TSource> (IEnumerable<TSource> first, IEnumerable<TSource> second)
    {
        foreach (TSource element in first)
            yield return element;
        foreach (TSource element in second)
            yield return element;
    }
    
于 2013-07-13T23:01:21.527 回答