問題描述
給定以下代碼片段:
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.
這篇關于轉換此迭代器塊如何進行功能更改?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!