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 是完全有效的:

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

这样您就可以确定它们都具有相同的效果。

于 2013-02-16T10:38:46.790 回答
4

其他答案有点错过了重点。

foreach如果 beeing 的(编译时)类型有一个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 一起使用(扩展方法 on IEnumerable<>),并且可以用作其他只需要IEnumerable<>.

此外,明智的做法是让返回非泛型IEnumerator的方法(或显式接口实现)直接调用返回的方法(或显式接口实现)IEnumerator<>。让两个返回不同的序列真的很令人困惑(但很适合询问和回答有关事物如何工作的问题)。

于 2015-02-02T19:20:36.373 回答
4

的非通用版本IEnumerable是通过显式接口实现来实现的。这意味着您只能通过转换为接口来调用显式实现的函数。

显式实现的原因IEnumerable是方法签名是相同的,除了返回类型。

myWords式转换为IEnumerable允许您像这样调用非泛型版本:(IEnumerable)myWords.

C# 指南解释了它是如何工作的:显式接口实现

于 2013-02-16T10:47:04.093 回答