如何判断IEnumerable是否会延迟执行?我总是假设如果我在LINQ对象的上下文中使用Select(x=>...),那么新集合将立即创建并保持静态。我不太确定我为什么这样做,这是一个非常糟糕的假设,但我做到了。我经常在别处使用.ToList(),但在这种情况下通常不使用。此代码演示即使是简单的“选择”也会延迟执行:varrandom=newRandom();varanimals=new[]{“猫”,“狗”,“老鼠”};varrandomNumberOfAnimals=animals.Select(x=>Math.Floor(random.NextDouble()*100)+""+x+"s");foreach(variinrandomNumberOfAnimals){testContextInstance.WriteLine("Thereare"+i);}}foreach(variinrandomNumberOfAnimals){testContextInstance.WriteLine("Andnow,thereare"+i);这将输出以下内容(每次迭代集合时调用随机函数):有75只猫有28只狗有62只老鼠现在有78只猫现在有69只狗现在有是43只老鼠我在很多地方都有一个IEnumerable作为班级成员。通常,LINQ查询的结果分配给此类IEnumerable。通常这对我来说不会造成问题,但我最近发现我的代码中有几个地方不仅仅是性能问题。在尝试检查我在哪里犯了这个错误时,我想我可以检查一个特定的IEnumerable是否属于IQueryable类型。我认为这会告诉我这些收藏是否“延期”。事实证明,上面的Select运算符创建的枚举器是System.Linq.Enumerable+WhereSelectArrayIterator``[System.String,System.String]类型,而不是IQueryable。我使用Reflector查看此接口继承了什么,结果发现它没有继承任何表明它是“LINQ”的东西——因此无法针对集合类型进行测试。我非常高兴现在可以将.ToArray()放在任何地方,但我想要一种机制来确保将来不会发生此问题。VisualStudio似乎知道如何执行此操作,因为它给出了有关“扩展结果视图将评估集合”的消息。我想出的最好的是:booldeferred=!object.ReferenceEquals(randomNumberOfAnimals.First(),randomNumberOfAnimals.First());编辑:这仅在使用“select”创建新对象时有效,不是通用解决方案。反正我不推荐!这是一个有点开玩笑的解决方案。LINQ的延迟执行困扰着很多人,而且您并不孤单。我为避免此问题采取的方法如下:方法参数-使用IEnumerable除非需要更具体的接口。局部变量——通常是在我创建LINQ时,所以我会知道惰性求值是否可行。类成员-从不使用IEnumerable,始终使用List。并始终将它们设为私有。属性-使用IEnumerable,并转换为setter中的存储。publicIEnumerablePeople{get{返回人;}设置{人=value.ToList();私人名单人;虽然有一种理论认为这行不通,但我还没有遇到过这种行不通的情况,而且自从测试版以来我一直是使用LINQ扩展方法的粉丝。顺便说一句:我很好奇你为什么要使用ToArray();而不是ToList();-对我来说列表有更好的API并且(几乎)没有性能成本。更新:一些评论者正确地指出数组具有理论上的性能优势,因此我将上述声明修改为“...几乎没有性能成本”。更新2:我写了一些代码来测试数组和列表之间的性能对差异进行一些微基准测试。在我的笔记本电脑上,在我的特定基准测试中,每次访问的差异约为5ns(即纳秒)。我想有些情况下每个循环节省5ns是值得的……但我从未遇到过。在运行时长到足以准确测量之前,我必须将我的测试添加到1亿次迭代。一般来说,我会说你应该尽量避免担心是否推迟。IEnumerable的流式执行特性有其优势。是的-有时它是不利的,但我建议始终专注于那些(罕见的)时间-ToList()或ToArray()将其转换为适当的列表或数组。剩下的时间,最好顺其自然。需要检查这似乎是一个更大的设计问题……我的五美分。很多时候你不得不处理一个你不知道里面有什么的枚举。您的选择是:这是一个示例:[TestClass]publicclassBadExample{publicclassItem{publicStringValue{get;放;}}publicIEnumerableSomebodysElseMethodWeHaveNoControlOver(){varvalues="最后一切都必须在上".Split('');返回values.Select(x=>newItem{Value=x});}[TestMethod]publicvoidTest(){varitems=this.SomebodysElseMethodWeHaveNoControlOver();foreach(variteminitems){item.Value=item.Value.ToUpper();}varmustBeInUpper=String.Join("",items.Select(x=>x.Value).ToArray());Trace.WriteLine(mustBeInUpper);//输出在下层:最后一切都必须在上层Assert.AreEqual("ATTHEENDEVERYTHINGMUSTBEINUPPER",mustBeInUpper);//所以没有办法摆脱它,但是:开箱即用地完全迭代它一次。使用相同的IEnumerable接口对于立即执行和延迟执行场景来说显然是一个糟糕的设计选择。两者之间必须有明确的区别,以便从名称或通过检查属性来确定可枚举对象是否被延迟。提示:考虑在您的代码中使用IReadOnlyCollection而不是普通的IEnumerable,因为除此之外您还可以获得Count属性。这样你就可以确定它不是无穷无尽的,你可以毫无问题地将它变成一个列表。有关评估集合的扩展结果视图的消息是为所有IEnumerable对象呈现的标准消息。我不确定是否有任何万无一失的方法来检查IEnumerable是否被推迟,主要是因为即使yield也被推迟了。绝对确定不延迟的唯一方法是接受ICollection或IList。手动实现惰性IEnumerator绝对是可能的,因此没有“完全通用”的方法来实现它。我必须记住的是:如果我在枚举与之相关的内容时更改列表的内容,请始终在foreach之前调用ToArray()。这是对惰性执行的有趣反应——大多数人认为这是积极的,因为它允许您转换数据流而无需缓冲所有内容。您建议的测试不会起作用,因为迭代器方法没有理由不能在连续两次尝试中产生与其第一个对象相同的引用对象实例。IEnumerableNames(){yieldreturn"Fred";这将每次返回相同的静态字符串对象作为序列中的唯一项目。由于您无法可靠地检测从迭代器方法返回的编译器生成的类,因此您必须执行相反的操作:检查一些众所周知的容器:publicstaticIEnumerableToNonDeferred(thisIEnumerablesource){if(sourceisList||sourceisT[])//以及你遇到的任何其他人returnsource;返回source.ToArray();通过返回IEnumerable,我们使集合保持只读状态,这很重要,因为我们可能会得到副本或原始集合。以上就是C#学习教程:如何判断IEnumerable是否会延迟执行?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
