为什么在包含yield return的函数中重复使用(){}块?
问题描述:
我有以下代码。查询需要大约30秒才能执行。我发现每次调用顶层using
块时for each
都会尝试枚举迭代器。对枚举器中的每个调用MoveNext
执行哪个查询。为什么在包含yield return的函数中重复使用(){}块?
那么为什么懒惰的评估一次又一次地重塑using
。我知道周围的工作,但我需要了解这个目的。
private IEnumerable<FriendProfile> GetProfiles() {
var query = @"
SELECT VirtualNumber, City, Gender, Nick, Age
FROM Profiles
WHERE Not VirtualNumber In (SELECT VirtualNumber FROM Messages)
ORDER BY Id DESC;";
using (var con = GetConnection()) {
using (var cmd = dbconnection.CreateCommand()) {
cmd.CommandText = query;
var reader = cmd.ExecuteReader();
while (reader.Read()) {
var fp = new FriendProfile();
fp.VirtualNumber = reader.GetString(0);
fp.City = reader.GetString(1);
fp.Gender = reader.GetString(2);
fp.Nick = reader.GetString(3);
fp.Age = reader.GetInt16(4).ToString();
yield return fp;
}
}
}
}
foreach(var fp in GetProfiles()){
.... //foreach item using(){} in GetProfile() is reinitializes. All usings blocks
}
答
我不是100%确定哪个使用了你所指的块,但是如果我理解正确,你所说的话不应该发生。 yield
将从GetProfiles()
方法返回执行控制,但yield
(即using
块的结尾)之后的代码将不会执行,直到while
条件为false
。
下面是一个简单的例子,应表现出这种行为:
使用此类显示当using
块的结束执行:
public class Disposable : IDisposable
{
private readonly string name;
public Disposable(string name)
{
this.name = name;
}
public void Dispose()
{
Console.WriteLine("Disposing of {0}", name);
}
}
和此代码:
private IEnumerable<int> Test()
{
using (new Disposable("outer"))
{
using (new Disposable("inner"))
{
for (int i = 0; i < 10; i++)
{
yield return i;
}
}
}
}
...
foreach (int i in Test())
{
Console.WriteLine("item {0}", i);
}
输出:
item 0 item 1 item 2 item 3 item 4 item 5 item 6 item 7 item 8 item 9 disposing of inner disposing of outer
这表明在退出for
循环之前,using
块不会退出。
答
这是因为yield
得到编译的方式。它实际上是一个迭代的调用,为了实现由GetProfiles()
方法定义的集合而创建的类。通过您的foreach
,每一步都会调用一次MoveNext()
。 This是一个体面的介绍幕后发生的事情。
+0
刚刚测试过它。最后(并因此使用)只被调用一次。可能当迭代器到达末端或被处置时。 – CodesInChaos 2010-11-27 20:54:49
你对我是一个错误。 `IEnumerable a = Test();`我在多个循环中使用了多个时间。为每个调用`a.GetEnumerator()`迭代,这就是为什么以前的枚举器是垃圾收集或处置。我应该保存`var b = Test()。GetEnumerator()`并多次使用它。 –
affan
2010-11-27 21:24:04