为什么在包含yield return的函数中重复调用using(){}块?

时间:2010-11-27 20:10:25

标签: c# .net

我有以下代码。查询需要大约30秒才能执行。我发现每次using尝试枚举迭代器时都会调用顶部for each块。在枚举器中对MoveNext的每次调用执行哪个均值查询。

那么为什么懒惰的评估会一遍又一遍地重新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
    }

2 个答案:

答案 0 :(得分: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

这表明using块在for循环退出之前不会退出。

答案 1 :(得分:0)

这是因为编译yield的方式。它实际上是对为实现由GetProfiles()方法定义的集合而创建的类的迭代调用。 MoveNext()中每个步骤都会调用foreach一次{{1}}。 This是对幕后发生的事情的一个不错的介绍。