linq concat是如何在引擎盖下工作的

本文关键字:工作 引擎 concat linq | 更新日期: 2023-09-27 18:22:24

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

        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)

有没有任何参考资料可以解释linqapi函数是如何为注重性能的人实现的?

谢谢!

编辑

好的,没有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

linq concat是如何在引擎盖下工作的

首先,您的代码只有在同时IEnumerable<T>上引入ForEach扩展方法的情况下才能工作。我建议你不要这么做——看看埃里克·利珀特的博客文章,原因我同意。

我建议你写为:

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

Concat执行得很好——它只是先迭代第一个序列,然后迭代第二个序列,在迭代过程中生成项。它不会缓冲所有元素。您添加的额外间接级别会对性能造成非常轻微的影响,但仅此而已。

我希望Mono也能有同样的表现——Concat非常简单。事实上,由于Mono是开源的,您可以自己检查它。(当然,它可能会随着时间的推移而移动…)

不久前,我在博客中详细介绍了LINQ to Objects,从头开始重新实现了整个过程,并记录了其性能的各个方面,包括哪里还有改进的空间。查看我的Edulinq博客系列了解更多详细信息。

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

Microsoft.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. 对于注重性能的人来说,源代码是最好的。

  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到对象:部分",所有这些文章都可以通过标签"LINQ"找到:链接

  2. Mono是一个开源项目。你可以在github上找到来源。可枚举类可以在以下位置创建:链接。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;
    }