枚举器行为会根据我们的引用方式而改变吗?

时间:2016-04-07 16:13:22

标签: c# ienumerable

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

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

我希望这可以打印&#34; 123&#34;,但它只打印零并且永不终止。具体类的相同示例:

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

发生了什么?

1 个答案:

答案 0 :(得分:6)

我对它进行了测试,对我而言,它也不适用于您的具体课程。

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

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

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

struct是一种值类型,因此每当您访问an.E时,您都会获得该值的副本

当您致电MoveNext()Current时,您可以在该副本上调用它,并且此副本会发生变异。

下次访问an.E以致电MoveNext()Current时,您会获得尚未迭代的枚举器新副本。

并且an.E.Current0而不是1因为 - 再次 - 你得到一个新的调查员MoveNext()尚未被调用。

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

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

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