9

Looking at this code :

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

Running :

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

Yields

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

I understand that the non-generic IEnumerator version is for compatibility.

Question:

  1. In what scenario will the non-generic be invoked?
  2. How can I force my code to be run with the non-generic IEnumerator?
4

3 に答える 3

7

非ジェネリックIEnumeratorは、コードがクラスを非ジェネリックインターフェイスにキャストするたびに実行されます。

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

これは、非ジェネリックIEnumeratorを必要とするレガシー関数にクラスを渡す場合に主に関連します。

したがって、関数を含むライブラリがあり、クラスをこの関数に渡す場合、非ジェネリック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を使用して非ジェネリックIEnumeratorを単純に実装することは完全に有効であることに注意してください。

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

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

于 2013-02-16T10:38:46.790 に答える
4

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

明示的に実装されている理由IEnumerableは、メソッドのシグネチャがリターンタイプを除いて同じであるためです。

myWordsに明示的にキャストIEnumerableすると、次のように非汎用バージョンを呼び出すことができます(IEnumerable)myWords

C#ガイドでは、これがどのように機能するかを説明しています。明示的なインターフェイスの実装

于 2013-02-16T10:47:04.093 に答える
4

他の答えは、ある意味、要点を見逃しています。

foreachビーイングされているものの(コンパイル時)タイプに、引数がゼロであるpublicと呼ばれる非ジェネリック非静的メソッドがある場合、インターフェースはまったく問題になりません。GetEnumerator(このメソッドの戻り型は、ジェネリックまたは非ジェネリックのいずれでもかまいません。インターフェースは重要ではありません。)

したがって、最初のメソッドが呼び出される理由は、これが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つの異なるシーケンスを返すことは、本当に混乱を招きます(ただし、物事がどのように機能するかについて質問したり、質問に答えたりするのに適しています)。

于 2015-02-02T19:20:36.373 に答える