2

同じ関数内のアイテムを追跡しないのに、他の関数で操作が発生したEnumerator場合は追跡しないのはなぜですか?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>.Enumeratorclassではなく、structです。GetEnumeratorは戻り値の型が であることを公開しているのでList<T>.Enumerator、 を使用するvarと、eの型はList<T>.Enumeratorであるため、 に渡すとIncremenet、自動的にボックス化されてIEnumerator<Int32>オブジェクトになります。これが、おかしな動作の原因です。

eとして入力するIEnumerator<Int32>と、オブジェクトを取得するとすぐにボックス化が行われるため、この奇妙な動作は発生しません。他のコードを inTestまたは inで実行しても同じように機能しますIncrement(ちなみに、そのメソッドのスペルは修正しました)。 、「インクリネット」ではありません)。

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 十分スマートであり、値型のセマンティクスを問題なく処理します。これまで見てきたように、変更可能なものは悪であるため、扱い方に細心の注意を払わないと混乱を引き起こします。MoveNextCurrentstruct

于 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 に答える