收益率如何成为可枚举对象?

27

我在玩弄yieldIEnumerable,现在我很好奇下面的代码片段为什么会起作用:

public class FakeList : IEnumerable<int>
{
    private int one;
    private int two;

    public IEnumerator<int> GetEnumerator()
    {
        yield return one;
        yield return two;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

现在编译器如何将这个转换:

public IEnumerator<int> GetEnumerator()
{
    yield return one;
    yield return two;
}

如何将其转换为 IEnumerator<int>


3
http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx - Matthew Watson
3个回答

33

使用yield return时,编译器会为您生成一个枚举器类。因此,实际使用的代码比仅有两个return语句要复杂得多。编译器将添加所有必要的代码,为您返回一个枚举器,该枚举器会遍历yield return产生的结果。


这是您的FakeList.GetEnumerator()所生成的代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

public class FakeList : IEnumerable<int>, IEnumerable
{
    private int one;
    private int two;

    [IteratorStateMachine(typeof(<GetEnumerator>d__2))]
    public IEnumerator<int> GetEnumerator()
    {
        yield return this.one;
        yield return this.two;
    }

    IEnumerator IEnumerable.GetEnumerator() => 
        this.GetEnumerator();

    [CompilerGenerated]
    private sealed class <GetEnumerator>d__2 : IEnumerator<int>, IDisposable, IEnumerator
    {
        private int <>1__state;
        private int <>2__current;
        public FakeList <>4__this;

        [DebuggerHidden]
        public <GetEnumerator>d__2(int <>1__state)
        {
            this.<>1__state = <>1__state;
        }

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

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

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

        [DebuggerHidden]
        void IEnumerator.Reset()
        {
            throw new NotSupportedException();
        }

        [DebuggerHidden]
        void IDisposable.Dispose()
        {
        }

        int IEnumerator<int>.Current =>
            this.<>2__current;

        object IEnumerator.Current =>
            this.<>2__current;
    }
}

你能看到 <GetEnumerator>d__2 类吗?它是基于你的两个 yield return 语句生成的。


13
当编译器看到 yield returnyield break 时,它将函数转换为实现状态机的类。调用该方法时会返回此类的一个实例。

C# In Depth 中有一节介绍代码的外观。


6

这是一个生成器。 我无法说明编译器的实际工作原理,但当您执行以下操作时:

yield return x;

你不会像传统的 return 一样离开函数,而是返回一个值,然后继续执行函数。
如果我没记错,真正的可枚举对象和这个有点不同,如果你不使用方法将生成器转换成真正的可枚举对象(取决于你想要实现什么功能),可能会遇到一些问题。

你能详细说明一下它们的区别吗?因为其他人提到编译器会生成可枚举实现。 - Mafii
我会尝试找回它。 - romain-aga
你找到了什么吗? - Mafii
1
我没有成功找回出现问题的情况。但是根据记忆,它与引用有关。我无法在生成的可枚举对象上进行两次枚举。但是当我进行测试时,一切都正常。我还有一件事要尝试,那就是使用扩展方法,行为可能会不同。 - romain-aga
我真的找不回来了。自从我修复它后,我就没有代码记得我是如何做到的。 - romain-aga

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接