C# 流式处理操作符与延迟执行有何不同?
在LINQC# 流式处理操作符与延迟执行有何不同?,c#,linq,C#,Linq,在LINQ中,其中是流式操作符。其中asOrderByDescending是非流式操作符。好吧,流媒体运营商只收集下一个需要的项目。非流式操作符一次评估整个数据流 我看不出定义流媒体运营商的相关性。对我来说,延迟执行是多余的。例如,我编写了一个自定义扩展,并使用where操作符和orderby使用它 public static class ExtensionStuff { public static IEnumerable<int> Where(this IEnumerabl
中,其中
是流式操作符。其中asOrderByDescending
是非流式操作符。好吧,流媒体运营商只收集下一个需要的项目。非流式操作符一次评估整个数据流
我看不出定义流媒体运营商的相关性。对我来说,延迟执行是多余的。例如,我编写了一个自定义扩展,并使用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);
}
公共静态类扩展插件
{
公共静态IEnumerable,其中(此IEnumerable序列,Func谓词)
{
foreach(按顺序输入i)
{
if(谓语(i))
{
收益率i;
}
}
}
}
公共静态void Main()
{
TestLinq3();
}
私有静态void TestLinq3()
{
int[]项={1,2,3,4};
所选变量=项目。其中(i=>i<3)
.OrderByDescending(i=>i);
写入(选定);
}
私有静态无效写入(已选择IEnumerable)
{
foreach(所选中的var i)
控制台写入线(i);
}
在这两种情况下,其中
需要评估每个元素,以确定哪些元素符合条件。它产生的事实似乎只有在操作员获得延迟执行的情况下才有意义
那么,流媒体运营商的重要性是什么呢?有两个方面:速度和内存 当您使用像
.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
之前只使用流式操作符,所以在Take
停止计算之前,您只能得到1到10的值。此外,一次只有一个值加载到内存中,因此内存占用非常小
在第二个示例中,OrderByDescending
不是流式处理,因此在提取第一项的那一刻,通过的整个结果必须放在内存中进行排序。这可能需要很长时间,并且会产生很大的内存占用
即使您没有使用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);
它的收益率似乎只是因为
运算符获得延迟执行
那么,流媒体运营商的重要性是什么
也就是说,您不能使用缓冲/非流扩展方法处理无限序列-而您可以仅使用流扩展方法“运行”这样的序列(直到您中止为止)
以这种方法为例:
public IEnumerable<int> GetNumbers(int start)
{
int num = start;
while(true)
{
yield return num;
num++;
}
}
OrderBy()
在这种情况下不起作用,因为它必须在发出单个数字之前穷尽地枚举结果。只是为了明确;在您提到的例子中,where-streams并没有什么好处,因为orderby无论如何都会把整个东西都吸进去。然而,有时使用流媒体的优势(其他答案/评论给出了示例),因此所有LINQ运营商都尽其所能流媒体。Orderby streams尽可能多地,而这恰好不是很多。其中流非常有效。在项中使用大约20亿整数重试。
@cHao或无限序列,或从开放网络流派生的序列。在某些情况下,Linq不能延迟执行。有些操作需要对IEnumerable进行slurping。不是Where(),而是一次一个。但是OrderBy是绝对的,除非您知道所有的集合项,否则无法对集合进行排序。我最喜欢的是Enumerable.Reverse(),它打破了IEnumerable规则。在IList上进行O(n)存储和O(n)执行时,没有得到令人震惊的优化。草率。挑剔,但它是无限的,只是每个结果都不是唯一的。它最终会溢出,但它会进行包装,因此它可以溢出无限次。(只要您不在选中的块中。)您可以只拥有while(true)yield return 4代码>如果你愿意。是否有LINQ操作符的流位于Where
和OrderBy
之间?@ZevSpitz我想这取决于你如何定义你的术语SkipWhile
可能符合条件,具体取决于您如何定义“流”的数量。将流式处理定义为能够一次解析一个结果(例如,Select
),而非流式处理需要所有值才能计算第一个值(例如,OrderBy
)。根据这个定义,为什么边跳
比选择
的流量要少呢?第一次迭代不需要所有的值。@ZevSpitz您使用的是严格的二进制定义,其中有些东西流或不流,而不是模拟定义,其中有些东西可以流到给定的程度SkipWhile
不需要使用所有输入序列来返回第一个项目,但它需要使用任意百分比的输入序列,在这一点上,它会切换到使用输入序列中的一个项目来返回每个结果。所以,这也是为什么我说这取决于你如何定义你的术语;在此上下文中没有一个定义明确的定义。根据您的逻辑,其中
应被视为间歇流式传输和非流式传输。但是确定是否将值传递给下一个操作符/foreach
的某些条件并不会从根本上改变查询的行为;当需要传递给其他运算符时,可以精确计算每个值OrderBy
是非常不同的——第一个结果需要对所有结果进行评估。
foreach (var num in GetNumbers(0).Where(x => x % 2 == 0))
{
Console.WriteLine(num);
}