开发者

Why using(){} block is repeatedly called in a function containing yield return?

I have following code. The query take some 30 seconds to execute. I found that the top using block is called every time for each try to enumerate the iterator. Which mean query is executed for each call to MoveNext in enumerator.

So why lazy evaluation reintialize using over and over again. I know the work around but i need to understand the purpose of this.

   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
    }


I'm not 100% sure which using block you are referring to, but if I have understood correctly, what you have said should not be happening. The yield will return control of execution from the GetProfiles() method, but the code after the yield (ie. the end of the using blocks) will not be executed until the while condition is false.

Here is a simple example that should demonstrate this behaviour:

Using this class to show when the end of the using block executes:

public class Disposable : IDisposable
{
    private readonly string name;

    public Disposable(string name)
    {
        this.name = name;
    }

    public void Dispose()
    {
        Console.WriteLine("Disposing of {0}", name);
    }
}

and this code:

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);
}

The output is:

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

This shows that the using blocks do not exit until the for loop exits.


It is because of the way yield gets compiled. It's actually an iterative call to a class created just to implement the collection defined by your GetProfiles() method. MoveNext() in that is called once per step through your foreach. This is a decent intro to what's going on behind the scenes.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜