何时调用IEnumerable.GetEnumerator而不是IEnumerable <t> .GetEnumerator?</t>

时间:2013-02-16 10:34:33

标签: c# .net ienumerable ienumerator

查看此代码:

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一起运行?

3 个答案:

答案 0 :(得分: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();
    }
}

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

答案 1 :(得分:4)

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

明确实现IEnumerable的原因是方法签名除了返回类型之外是相同的。

myWords明确地投射到IEnumerable,您可以调用非通用版本,如下所示:(IEnumerable)myWords

C#指南解释了其工作原理:Explicit interface implementation

答案 2 :(得分:4)

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

如果foreach ed的(编译时)类型有一个名为public的{​​{1}}非泛型非静态方法,则接口无关紧要参数。 (此方法的返回类型可以是任何类型,通用或非泛型:接口无关紧要。)

因此调用第一个方法的原因是这是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(); } } 是明智之举。然后您的类型可以与Linq一起使用(IEnumerable<>上的扩展方法),并且可以用作仅需要IEnumerable<>的其他方法的参数。

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

相关问题