问题描述
给定以下代码片段:
public class Foo
{
public IEnumerable<string> Sequence { get; set; }
public IEnumerable<string> Bar()
{
foreach (string s in Sequence)
yield return s;
}
}
下面的代码片段在语义上是等价的,还是不同的?如果不同,它们的功能有何不同?
is the following snippet semantically equivalent, or is it different? If it is different, how do they function differently?
public class Foo2
{
public IEnumerable<string> Sequence { get; set; }
public IEnumerable<string> Bar2()
{
return Sequence;
}
}
此问题的灵感来自 this question,该问题针对类似情况提出了不同的问题.
This question is inspired by this question which is asking a different question about a similar situation.
推荐答案
两者不等价.两个 Bar
方法之间如何延迟执行的语义是不同的.当您调用 Bar
时,Foo.Bar
会将 Sequence
评估为 IEnumerable
值.当您枚举 Bar2
返回的序列时,Foo2.Bar2
会将 Sequence
评估为该变量 中的值.
The two are not equivalent. The semantics of how execution is deferred between the two Bar
methods is different. Foo.Bar
will evaluate Sequence
into an IEnumerable
value when you call Bar
. Foo2.Bar2
will evaluate Sequence
into the value in that variable when you enumerate the sequence returned by Bar2
.
我们可以编写一个足够简单的程序来观察这里的差异.
We can write a simple enough program to observe the differences here.
//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
方法也没有副作用.如果确实如此,那么理解程序具有的语义以及它应该具有的语义就不会变得更加重要.例如,让我们修改这两个方法,使其具有一些可观察到的副作用:
In this particular case our Bar
method also has no side effects. If it did it would not be noticeably more important to understand the semantics that your program has, and what it should have. For example, let's modify the two methods so that they have some observable side effects:
public class Foo
{
public IEnumerable<string> Sequence { get; set; }
public IEnumerable<string> IteratorBlock()
{
Console.WriteLine("I'm iterating Sequence in an iterator block");
foreach (string s in Sequence)
yield return s;
}
public IEnumerable<string> NoIteratorBlock()
{
Console.WriteLine("I'm iterating Sequence without an iterator block");
return Sequence;
}
}
现在让我们尝试比较这两种方法,看看它们是如何工作的:
Now let's try comparing these two methods to see how they function:
var query = foo.IteratorBlock();
var query2 = foo.NoIteratorBlock();
Console.WriteLine("---");
query.Count();
query.Count();
query2.Count();
query2.Count();
这将打印出来:
我在没有迭代器块的情况下迭代序列
---
我正在迭代器块中迭代序列
我正在迭代器块中迭代序列
I'm iterating Sequence without an iterator block
---
I'm iterating Sequence in an iterator block
I'm iterating Sequence in an iterator block
这里我们可以看到非迭代器块的副作用发生在方法本身被调用时,而迭代器块的副作用不会在那个时间点发生.然后,稍后,每次我们迭代非迭代器块时,它根本不会引起副作用,但迭代器块每次迭代查询时都会引起副作用.
Here we can see that the non-iterator block's side effects happen when the method itself is called, and the iterator block's side effects don't happen at that point in time. Then, later on, each time we iterate the non-iterator block it doesn't cause the side effects at all, but the iterator block causes the side effects each time the query is iterated.
这篇关于转换此迭代器块如何进行功能更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!