2

为什么 anEnumerator不跟踪同一函数中的项目,但如果MoveNext 操作发生在其他函数中则不跟踪?

例子:

    public static void Test()
    {
        var array = new List<Int32>(new Int32[] { 1, 2, 3, 4, 5 });
        var e = array.GetEnumerator();
        e.MoveNext();
        e.MoveNext();
        Console.WriteLine(e.Current); // 2
        Incremenet(e);
        Console.WriteLine(e.Current); //2
    }

    static void Incremenet(IEnumerator<Int32> e)
    {
        Console.WriteLine("Inside " + e.Current); //2
        e.MoveNext();
        Console.WriteLine("Inside " + e.Current); // 3
        e.MoveNext();
        Console.WriteLine("Inside " + e.Current); //4
    }

我期待在最后一个 CW 中得到 5,但我得到了 2,就像它从未增加一样。为什么函数返回时忘记了函数MoveNext内部?Increment

干杯。

4

2 回答 2

6

List<T>的枚举数类型List<T>.Enumerator不是 a class,而是 a struct。由于GetEnumerator暴露了返回类型是List<T>.Enumerator,当你使用var,e的类型是List<T>.Enumerator,所以当你将它传递给 时Incremenet,它会被自动装箱为一个IEnumerator<Int32>对象。这是您看到的奇怪行为的原因。

如果您键入ean IEnumerator<Int32>,则在获得对象后立即发生装箱,因此不会发生这种奇怪的行为:无论您在其中Test还是在其中运行其他代码,它的工作原理都是一样的Increment(顺便说一下,我修复了该方法的拼写,它不是“Incremenet”)。

public static void Test()
{
    var array = new List<Int32> { 1, 2, 3, 4, 5 };
    IEnumerator<Int32> e = array.GetEnumerator(); // boxed here
    e.MoveNext();
    e.MoveNext();
    Console.WriteLine(e.Current); // 2
    Increment(e);
    Console.WriteLine(e.Current); // now it's 4
}

static void Increment(IEnumerator<Int32> e)
{
    Console.WriteLine("Inside " + e.Current); // 2
    e.MoveNext();
    Console.WriteLine("Inside " + e.Current); // 3
    e.MoveNext();
    Console.WriteLine("Inside " + e.Current); // 4
}

它作为它的类型而不是IEnumerator<T>出于性能原因公开。foreach 在这种情况下,它足够聪明,无需装箱或虚拟调度即可调用MoveNext,并且Current可以毫无问题地处理值类型语义。正如您所看到的,当您不非常注意如何处理它时,它确实会引起混乱,因为mutablestruct是邪恶的

于 2013-10-30T16:41:03.583 回答
1

出于同样的原因test,在以下测试用例中递增后为 1。这是值类型的正常行为。

    static void Main(string[] args)
    {
        int test = 1;
        Increment(test);
        Console.WriteLine("After increment: " + test);
    }

    static void Increment(int test)//add ref and the original variable will also update
    {
        test += 1;
        Console.WriteLine(test);
    }

正如 Servy 从技术上指出的那样,该示例的不同之处在于局部变量test是不可变的。实际上,我们看到的行为是因为变量被复制到了Increment方法中。但是,我的观点是,这种类型的行为在值类型(属性和局部变量)之间是一致的。有关这一事实的进一步证据:

struct MutableStruct
{
    public int EvilInt { get; set; }    
}

class Program
{        
    static void Main(string[] args)
    {
        var testStruct = new MutableStruct();
        testStruct.EvilInt = 1;

        int test = 1;
        Increment(test, testStruct);
        Console.WriteLine("After increment: " + test + " and..." + testStruct.EvilInt);//both 1
    }

    static void Increment(int test, MutableStruct test2)
    {
        test2.EvilInt += 1;
        test += 1;
        Console.WriteLine(test + " and..." + test2.EvilInt);//both 2
    }
}

正如我们在这里看到的,这种行为在值类型中是正常的。在局部不可变值类型和可变结构的情况下,行为保持一致。

于 2013-10-30T16:38:17.160 回答