Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
685 views
in Technique[技术] by (71.8m points)

c# - How yield implements the pattern of lazy loading?

How yield implements the pattern of lazy loading?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

yield implementation doesn't reach the code until it's needed.

For example, this code:

public IEnumerable<int> GetInts()
{
    yield return 1;
    yield return 2;
    yield return 3;
}

Will actually compile into a nested class which implements IEnumerable<int> and the body of GetInts() will return an instance of that class.

Using reflector you can see:

public IEnumerable<int> GetInts()
{
    <GetInts>d__6d d__d = new <GetInts>d__6d(-2);
    d__d.<>4__this = this;
    return d__d;
}

Edit - adding more info about GetInts implementation:
The way this implementation makes it lazy is based on the Enumerator MoveNext() method. When the enumerable nested class is generated (<GetInts>d__6d in the example), it has a state and to each state a value is connected (this is a simple case, in more advanced cases the value will be evaluated when the code reach the state). If we take a look in the MoveNext() code of <GetInts>d__6d we'll see the state:

private bool MoveNext()
{
    switch (this.<>1__state)
    {
        case 0:
            this.<>1__state = -1;
            this.<>2__current = 1;
            this.<>1__state = 1;
            return true;

        case 1:
            this.<>1__state = -1;
            this.<>2__current = 2;
            this.<>1__state = 2;
            return true;

        case 2:
            this.<>1__state = -1;
            this.<>2__current = 3;
            this.<>1__state = 3;
            return true;

        case 3:
            this.<>1__state = -1;
            break;
    }
    return false;
}

When the enumerator is asked for the current object it returns the object which is connected to the current state.

In order to show that the code is evaluated only when it's required you can look at this example:

[TestFixture]
public class YieldExample
{
    private int flag = 0;
    public IEnumerable<int> GetInts()
    {
        yield return 1;
        flag = 1;
        yield return 2;
        flag = 2;
        yield return 3;
        flag = 3;
    }

    [Test]
    public void Test()
    {
        int expectedFlag = 0;
        foreach (var i in GetInts())
        {
            Assert.That(flag, Is.EqualTo(expectedFlag));
            expectedFlag++;
        }

        Assert.That(flag, Is.EqualTo(expectedFlag));
    }
}

I hope it's a bit more clear. I recommend to take a look at the code with Reflector and observe the compiled code as you change the "yield" code.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...