如何转换此迭代器阻止function更改?

给出以下代码片段:

public class Foo { public IEnumerable Sequence { get; set; } public IEnumerable Bar() { foreach (string s in Sequence) yield return s; } } 

以下代码片段在语义上是等价的,还是不同? 如果它不同,它们如何以不同的方式运作?

 public class Foo2 { public IEnumerable Sequence { get; set; } public IEnumerable Bar2() { return Sequence; } } 

这个问题的灵感来自于这个问题 ,它提出了一个类似情况的不同问题。

这两者并不相同。 两种Bar方法之间的执行延迟的语义是不同的。 当你调用Bar时, Foo.Bar会将Sequence计算为IEnumerable值。 当枚举Bar2返回的序列时Foo2.Bar2会将Sequence计算为该变量中的值。

我们可以编写一个足够简单的程序来观察这里的差异。

 //Using iterator block var foo = new Foo(); foo.Sequence = new[] { "Old" }; var query = foo.Bar(); foo.Sequence = new[] { "New" }; Console.WriteLine(string.Join(" ", query)); //Not using iterator block var foo2 = new Foo2(); foo2.Sequence = new[] { "Old" }; var query2 = foo2.Bar2(); foo2.Sequence = new[] { "New" }; Console.WriteLine(string.Join(" ", query2)); 

打印出:


在这种特殊情况下,我们的Bar方法也没有副作用。 如果确实如此,理解程序所具有的语义以及应该具有的语义就不那么重要了。 例如,让我们修改这两个方法,使它们有一些可观察到的副作用:

 public class Foo { public IEnumerable Sequence { get; set; } public IEnumerable IteratorBlock() { Console.WriteLine("I'm iterating Sequence in an iterator block"); foreach (string s in Sequence) yield return s; } public IEnumerable NoIteratorBlock() { Console.WriteLine("I'm iterating Sequence without an iterator block"); return Sequence; } } 

现在让我们尝试比较这两种方法,看看它们是如何运作的:

 var query = foo.IteratorBlock(); var query2 = foo.NoIteratorBlock(); Console.WriteLine("---"); query.Count(); query.Count(); query2.Count(); query2.Count(); 

这将打印出来:

我在没有迭代器块的情况下迭代Sequence

我在迭代器块中迭代Sequence
我在迭代器块中迭代Sequence

在这里我们可以看到非迭代器块的副作用在调用方法本身时发生,并且迭代器块的副作用不会在那个时间点发生。 然后,稍后,每次迭代非迭代器块时都不会产生副作用,但迭代器块会在每次迭代查询时产生副作用