9

在 LINQWhere中是一个流式操作符。Where-asOrderByDescending是非流媒体运营商。AFAIK,流媒体运营商只收集下一个必要的项目。非流式操作符一次评估整个数据流。

我看不到定义 Streaming Operator 的相关性。对我来说,延迟执行是多余的。以我编写了一个自定义扩展并使用 where 运算符和 orderby 使用它的示例为例。

public static class ExtensionStuff
{
    public static IEnumerable<int> Where(this IEnumerable<int> sequence, Func<int, bool> predicate)
    {
        foreach (int i in sequence)
        {
            if (predicate(i))
            {
                yield return i;
            }
        }
    }
}

    public static void Main()
    {
        TestLinq3();
    }

    private static void TestLinq3()
    {
        int[] items = { 1, 2, 3,4 };

        var selected = items.Where(i => i < 3)
                            .OrderByDescending(i => i);

        Write(selected);
    }



    private static void Write(IEnumerable<int> selected)
    {
        foreach(var i in selected)
            Console.WriteLine(i);
    }

无论哪种情况,Where都需要评估每个元素以确定哪些元素满足条件。它产生的事实似乎只是因为操作员获得了延迟执行而变得相关。

那么,流媒体运营商的重要性是什么?

4

3 回答 3

15

有两个方面:速度和内存。

当您使用.Take()仅使用部分原始结果集的方法时,速度方面变得更加明显。

// Consumes ten elements, yields 5 results.
Enumerable.Range(1, 1000000).Where(i => i % 2 == 0)
    .Take(5)
    .ToList();

// Consumes one million elements, yields 5 results.
Enumerable.Range(1, 1000000).Where(i => i % 2 == 0)
    .OrderByDescending(i => i)
    .Take(5)
    .ToList();

因为第一个示例在调用 之前仅使用流式运算符,所以在停止评估Take之前您最终只能产生 1 到 10 的值。Take此外,一次仅将一个值加载到内存中,因此您的内存占用量非常小。

在第二个示例中,OrderByDescending不是流式传输,因此在 Take 拉出第一项的那一刻,通过Where过滤器的整个结果必须放在内存中进行排序。这可能需要很长时间并产生很大的内存占用。

即使您没有使用Take,内存问题也可能很重要。例如:

// Puts half a million elements in memory, sorts, then outputs them.
var numbers = Enumerable.Range(1, 1000000).Where(i => i % 2 == 0)
    .OrderByDescending(i => i);
foreach(var number in numbers) Console.WriteLine(number);

// Puts one element in memory at a time.
var numbers = Enumerable.Range(1, 1000000).Where(i => i % 2 == 0);
foreach(var number in numbers) Console.WriteLine(number);
于 2012-04-05T21:11:45.903 回答
2

它产生的事实似乎只是因为操作员获得了延迟执行而变得相关。

那么,流媒体运营商的重要性是什么?

即,您无法使用缓冲/非流扩展方法处理无限序列 - 而您可以仅使用流扩展方法“运行”这样的序列(直到您中止)。

以这种方法为例:

public IEnumerable<int> GetNumbers(int start)
{
    int num = start;

    while(true)
    {
        yield return num;
        num++;
    }
}

你可以Where很好地使用:

foreach (var num in GetNumbers(0).Where(x => x % 2 == 0))
{
    Console.WriteLine(num);
}

OrderBy()在这种情况下将不起作用,因为它必须在发出单个数字之前详尽地枚举结果。

于 2012-04-05T21:12:25.170 回答
2

只是为了明确; 在您提到的情况下, where 流的事实没有任何优势,因为无论如何 orderby 都会吸收整个事情。但是,有时会使用流式传输的优势(其他答案/评论已给出示例),因此所有 LINQ 运算符都会尽其所能进行流式传输。Orderby尽可能多地流式传输,而这恰好不是很多。流非常有效的地方。

于 2012-04-05T21:26:28.630 回答