枚举器行为的变化基于我们如何引用它?

Enumerator behavior changes based on how we reference it?

将对列表枚举器的引用包装在 class 中似乎改变了它的行为。匿名示例 class:

public static void Main()
{
    var list = new List<int>() { 1, 2, 3 };
    var an = new { E = list.GetEnumerator() };
    while (an.E.MoveNext())
    {
        Debug.Write(an.E.Current);
    }
}

我希望它打印“123”,但它只打印零并且永远不会终止。同一个具体的例子 class:

public static void Main()
{
    var list = new List<int>() { 1, 2, 3 };
    var an = new Foo()
    {
        E = list.GetEnumerator()
    };

    while (an.E.MoveNext())
    {
        Debug.Write(an.E.Current);
    }
}

public class Foo
{
    public List<int>.Enumerator E { get; set; }
}

怎么回事?

我测试了它,对我来说它也不适用于你的混凝土 class。

原因是 List<T>.Enumerator 是一个 可变的 structan.E 是一个 属性 .

编译器为每个 auto-属性 生成一个支持字段,如下所示:

public class Foo
{
    private List<int>.Enumerator _E;
    public List<int>.Enumerator get_E() { return E; }
    public void set_E(List<int>.Enumerator value) { E = value; }
}

A struct 是一种值类型,因此每次访问 an.E 时,您都会得到该值的 copy

当您调用 MoveNext()Current 时,您在 那个副本 上调用它,并且这个副本发生了变异。

下次您访问 an.E 以调用 MoveNext()Current 时,您会得到一个 尚未迭代的枚举器的新副本 .

并且 an.E.Current0 而不是 1 因为 - 再一次 - 你得到一个新的枚举器 MoveNext() 还没有被调用。


如果您想存储列表枚举器的 reference,您可以使用 属性 类型声明 class Foo IEnumerator<int>:

public class Foo
{
    public IEnumerator<int> E { get; set; }
}

如果您现在分配 E = list.GetEnumerator();,枚举器将被装箱并存储 reference 而不是 value