2013-02-16 8 views
8

は、このコードを見てみると:いつIEnumerable.GetEnumeratorがIEnumerableの代わりに呼び出されるのですか。<T> .GetEnumerator?

public class myWords : IEnumerable<string> 
{ 
    string[] f = "I love you".Split(new string[]{"lo"},StringSplitOptions.RemoveEmptyEntries); 

    public IEnumerator<string> GetEnumerator() 
    { 
     return f.Select(s => s + "2").GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return f.Select(s => s + "3").GetEnumerator(); 
    } 
} 

実行:

myWords m = new myWords(); 
foreach (var s in m) 
{ 
    Console.WriteLine(s); 
} 

利回り

I 2 
ve you2 // notice "2", so the generic Ienumerator has executed. 

私は非ジェネリックIEnumeratorバージョンは互換性のためであることを理解しています。

質問:非ジェネリック呼び出されます

  1. どんなシナリオでは?
  2. どうすればいいですか私のコードは非汎用のIEnumeratorで実行されますか?コードは、非ジェネリックインターフェイスにクラスを唱えるたび
+0

'Enumerable.Cast' – CodesInChaos

答えて

3

他の回答は一種の欠点です。 foreach EDはゼロの引数を取りGetEnumerator呼ばpublic非ジェネリック非静的メソッドを持ってbeeingているものの(コンパイル時)型の場合

インタフェースは全く関係ありません。 (このメソッドの戻り値の型は、汎用でも非汎用でもかまいません。インタフェースは重要ではありません。)

だからあなたのメソッドの最初が呼び出された理由は、これはpublic方法であるということです。

あなたはそれを変更することができます

public class myWords : IEnumerable<string> 
{ 
    string[] f = "I love you".Split(new string[]{"lo"},StringSplitOptions.RemoveEmptyEntries); 

    IEnumerator<string> IEnumerable<string>.GetEnumerator() 
    { 
     return f.Select(s => s + "2").GetEnumerator(); 
    } 

    public IEnumerator GetEnumerator() 
    { 
     return f.Select(s => s + "3").GetEnumerator(); 
    } 
} 

はインターフェースが必要でないことを証明するために、これを試してみてください。

public class myWords // no interfaces! 
{ 
    string[] f = "I love you".Split(new string[]{"lo"},StringSplitOptions.RemoveEmptyEntries); 

    public IEnumerator GetEnumerator() 
    { 
     return f.Select(s => s + "3").GetEnumerator(); 
    } 
} 

しかし、IEnumerable<>を実装するのが賢明です。そして、あなたのタイプは、LINQの(IEnumerable<>上の拡張メソッド)を使用することができ、単にIEnumerable<>を必要とする他の方法への引数として使用することができます。

IEnumerator<>を返すメソッド(または明示的なインターフェイスの実装)にコールするだけで、非汎用のIEnumeratorを返すメソッド(または明示的なインターフェイスの実装)を使用することをお勧めします。 2つの戻り値の異なるシーケンスを持つことは、実際には混乱します(しかし、どのように動作するかについての質問と答えにはうってつけです)。

+0

インタフェースが必要ないことを示したいのであれば、 'Current'プロパティと' MoveNext'方法がありますが、任意のインタフェースを実装していない構造を導入提案、およびIEnumerable' 'のではなく、入力し' myWords'」 'GetEnumerator'メソッドの戻り値を持っているdは。 "IEnumerator'ではなく" – supercat

+0

OK "と言っています。私が言いたいのは、C#コンパイラが指定した条件で 'public'メソッドを実行するということでした。そのため、インターフェイスがあるかどうか、インターフェイスがマップされているかどうかは関係ありません。私は、foreachが意図どおり/期待どおりに進めると言っていませんでした。私はあなたの提案を試みようとしています。 –

+0

@supercat私が書いたのはうまくいった。この: 'クラスドーム { 公共命じGetEnumeratorメソッド(){ リターンデフォルト(命じました)。 }} 構造体は {USHORT状態を命じました。 パブリックブールのMoveNext(){ ++状態。 がtrueを返します。 } 公開ストリングCurrent { get { return arr [state%4]; } } static readonly string [] arr = {"a"、 "b"、 "c"、 "d"、}; } '新しいDoMeとforeachを作成すると動作します。私は何を示しましたか? –

7

非ジェネリックのIEnumeratorが実行されます:あなたは非を必要とするいくつかの従来の関数に自分のクラスを渡す場合

((IEnumerable)myWords).GetEnumerator(); // this calls the non-generic one 

は、これは主に関連しています一般的なIEnumerator。

あなたは関数を含むいくつかのライブラリを持っていて、この関数にクラスを渡すのであれば、単に実装することは完全に有効であることに注意してください

DoSomeStuffWithAnIEnumerable(IEnumerable x) 
{ 
    var foo = x.GetEnumerator(); 

    // or, as stackx said, an example with foreach: 
    foreach (var foo2 in x) 
    Console.WriteLine(foo2); 
} 


DoSomeStuffWithAnIEnumerable(new myWords()); 


非ジェネリックのIEnumerator

を使用します。

public class myWords : IEnumerable<string> 
{ 
    ....  
    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 

このようにすれば、両方が同じ効果を持つことが確認できます。

+1

また、foreachを使って例を挙げてみましょう:foreach(IEnumerable)myWordsのオブジェクトワード)... – stakx

4

IEnumerableの非汎用バージョンは、明示的なインターフェイス実装で実装されています。これは、明示的に実装された関数をインターフェイスにキャストすることによってのみ呼び出すことができることを意味します。

IEnumerableが明示的に実装されている理由は、戻り値の型を除いてメソッドシグネチャが同じであるためです。

IEnumerableに明示的にキャストmyWordsは、(IEnumerable)myWordsのような非ジェネリック版を呼び出すことができます。

C#ガイドでは、この動作の仕組みについて説明しています。Explicit interface implementation

関連する問題