代码之家  ›  专栏  ›  技术社区  ›  masoud ramezani

yield如何实现延迟加载模式?

  •  9
  • masoud ramezani  · 技术社区  · 15 年前

    怎么用? yield 实现的模式 lazy loading ?

    3 回复  |  直到 15 年前
        1
  •  13
  •   Elisha    15 年前

    在需要之前,yield实现不会到达代码。

    例如,此代码:

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

    将实际编译为一个嵌套类,该类实现 IEnumerable<int> 以及 GetInts() 将返回该类的实例。

    使用反射镜可以看到:

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

    编辑 -添加有关的详细信息 GetInts 实施:
    此实现使其变懒的方式基于 Enumerator MoveNext() 方法。生成可枚举嵌套类时( <GetInts>d__6d 在本例中),它有一个状态,每个状态都连接一个值(这是一个简单的情况,在更高级的情况下,当代码到达该状态时,将计算该值)。如果我们看看 移动下一个() 代码 <获取>d\u 6d 我们将看到国家:

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

    当要求枚举器提供当前对象时,它返回连接到当前状态的对象。

    为了表明仅在需要时才计算代码,您可以查看以下示例:

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

    我希望能再清楚一点。我建议使用reflector查看代码,并在更改“yield”代码时观察编译后的代码。

        2
  •  7
  •   gammelgul    15 年前

    如果您想知道编译器在使用 yield return ,查看jon skeet的文章: Iterator block implementation details

        3
  •  4
  •   peSHIr    15 年前

    基本上迭代器通过使用 yield 语句被编译成实现 state machine .

    如果你从来没有 foreach (=遍历并实际使用)返回的 IEnumerable<T> ,代码从未实际执行。如果这样做了,那么只执行确定要返回的下一个值所需的最小代码,只在请求下一个值时恢复执行。

    当您在调试器中单步执行此类代码时,实际上可以看到这种行为发生。至少尝试一次:我认为看到这一步一步发生是很有启发性的。