2013-09-30 11 views
6

実際には、IEnumerator.Reset method should never be usedList<T>のメソッド実装の奇妙な動作が見つかりました。リスト<T> .Enumerator IEnumerator.Reset()メソッドの実装

は、どんなにあなたが.NET Frameworkのソースコードを調べる方法を、以下のような方法が実装されている(参照元とILSpyで試した):

void System.Collections.IEnumerator.Reset() { 
    if (version != list._version) { 
     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); 
    } 

    index = 0; 
    current = default(T); 
} 

しかし、それはすべてのメソッドが呼び出されないように見えます!それは二回True3を印刷する必要があること、それはかなり明確だ

var list = new List<int>(1) { 3 }; 
using (var e = list.GetEnumerator()) 
{ 
    Console.WriteLine(e.MoveNext()); 
    Console.WriteLine(e.Current); 

    ((IEnumerator)e).Reset(); 

    Console.WriteLine(e.MoveNext()); 
    Console.WriteLine(e.Current); 
} 

:コードを考えてみましょう。その代わりに結果は

True 
3 
False 
0 

私は行方不明ですか?

+0

が変更可能な構造体は悪である理由の新しい一例であることが判明しました。明示的なインターフェース実装を通して突然変異が起こる変更可能な構造体はちょっとした悪事です。しかし、これらのネストされた 'Enumerator'構造体は通常、' foreach'文を翻訳するときにC#コンパイラによってのみ使用されます。 –

答えて

14

私は行方不明ですか?

はい:あなたはList.Enumeratorここにボクシングしている:

既存の コピーを取り、それをリセット
((IEnumerator)e).Reset(); 

- 一枚でオリジナルを残します。

実際列挙子をリセットするには、あなたがこのようなものが必要だろう:それは、明示的なインターフェイスの実装を使用しているため

var list = new List<int>(1) { 3 }; 
var e = list.GetEnumerator(); 
// Can't use "ref" with a using statement 
try 
{ 
    Console.WriteLine(e.MoveNext()); 
    Console.WriteLine(e.Current); 

    Reset(ref e); 

    Console.WriteLine(e.MoveNext()); 
    Console.WriteLine(e.Current); 
} 
finally 
{ 
    e.Dispose(); 
} 

static void Reset<T>(ref T enumerator) where T : IEnumerator 
{ 
    enumerator.Reset(); 
} 

それはトリッキーです。

私はそれをテストしていませんが、あなたのためにはうまくいくと思います。明らかにそれはこれを行うに悪いアイデア...

EDITです:代わりに、ただで開始するIEnumeratorまたはIEnumerator<int>にあなたの変数の型を変更します。そして、それは一度を箱詰めされ、Reset方法は箱入り値変異します:

var list = new List<int>(1) { 3 }; 
using (IEnumerator e = list.GetEnumerator()) 
{ 
    Console.WriteLine(e.MoveNext()); 
    Console.WriteLine(e.Current); 

    e.Reset(); 

    Console.WriteLine(e.MoveNext()); 
    Console.WriteLine(e.Current); 
} 
+0

あなたはまた、参照によってそれを渡すために '使用する 'から取り除く必要があります。その変更以外に、私はそれを本当に素早くテストしました。これはうまくいきます。 – Servy

+1

@Servyは正しいです。彼は最初にそれをボックスに入れてから、同じボックスをずっと使い続けることができます。編集:例えば、最初に 'List <>'をインターフェースタイプ 'IEnumerable <>'または 'IEnumerable'にアップキャストし、そのインターフェースの' GetEnumerator() 'メソッドを使うと、列挙子は取得時にボックスに入っていますそして、彼は例全体を通してそのボックスを再利用することができます。 –

+0

確かに、あなたは正しいです。私は 'IEnumerator'を変数に代入してテストしましたが、実際にはリセットされています。ありがとう。私はその答えをできるだけ早く受け入れられるものとしてマークします。 – MarcinJuraszek

関連する問題