如何转换此迭代器阻止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
在这里我们可以看到非迭代器块的副作用在调用方法本身时发生,并且迭代器块的副作用不会在那个时间点发生。 然后,稍后,每次迭代非迭代器块时都不会产生副作用,但迭代器块会在每次迭代查询时产生副作用 。