2010-11-27 66 views
2

我有以下代码。查询需要大约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 
    } 

回答

4

我不是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块不会退出。

+0

你对我是一个错误。 `IEnumerable a = Test();`我在多个循环中使用了多个时间。为每个调用`a.GetEnumerator()`迭代,这就是为什么以前的枚举器是垃圾收集或处置。我应该保存`var b = Test()。GetEnumerator()`并多次使用它。 – affan 2010-11-27 21:24:04

0

这是因为yield得到编译的方式。它实际上是一个迭代的调用,为了实现由GetProfiles()方法定义的集合而创建的类。通过您的foreach,每一步都会调用一次MoveNext()This是一个体面的介绍幕后发生的事情。

+0

刚刚测试过它。最后(并因此使用)只被调用一次。可能当迭代器到达末端或被处置时。 – CodesInChaos 2010-11-27 20:54:49

相关问题