IEnumerable.GetEnumerator() 和 IEnumerable<T>.GetEnumerator()

7
在 .net 框架中,有一个泛型接口 IEnumerable<T>,它继承自非泛型的 IEnumerable,它们都有一个 GetEnumerator() 方法。这两个 GetEnumerator() 唯一的区别在于返回类型。 现在我有一个类似的设计,但是当我编译代码时,编译器会报错: MyInterface<T>.GetItem() 返回具体类型 T,而 MyInterface.GetItem() 返回类型为 System.Object。 因此,我认为如果 BCL 团队编译 .net 框架,他们也会得到同样的警告。 我认为编译器警告不好,你怎么看?我该如何解决这个问题?我的意思是,我想在调用 MyInterface<T>.GetItem() 时获取具体类型 T,而不仅仅是 System.Object 类型的实例。 谢谢!:-) 补充说明: 我指的是接口本身:IMyInterface 继承自 IMyInterface,并且它们都有 GetItem() 方法(IMyInterface.GetItem() 返回类型为 T,而 IMyInterface.GetItem() 返回类型为 System.Object)。问题是,如果我们的代码只有这两个接口,即没有派生的具体类,在编译源代码后将遇到编译器警告。
2个回答

11
他们这么做是因为他们将一个版本编译成明确的接口方法实现。它看起来像这样:
public class SomeClassThatIsIEnumerable<T> : IEnumerable<T>
{
    public IEnumerator<T> GetEnumerator()
    {
       // return enumerator.
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable<T>)this).GetEnumerator();
    }
}

这种构造方式的作用是使第一个GetEnumerator方法成为默认方法,而另一个GetEnumerator方法只有在调用者首先将SomeClassThatIsIEnumerable强制转换为IEnumerator类型时才能访问,从而避免了问题。

编辑:根据上面的补充说明,您需要使用new关键字:

public interface IMyInterface
{
   object GetObject();
}

public interface IMyInterface<T> : IMyInterface
{
   new T GetObject();
}

// implementation:

public class MyClass : IMyInterface<string>
{
    public string GetObject() 
    {
        return "howdy!";
    }

    object IMyInterface.GetObject()
    {
        return GetObject();
    }
}

嗨,我不是在说这个,请看我在帖子中的“补充”部分。谢谢 :) - Mouhong Lin
即使无法为显式接口实现指定访问级别,仍应返回+1。 - erikkallen
@Dylan:按照编译器的建议使用new关键字。请参见我上面的编辑。 - David Morton
可以,谢谢。但是当我们使用Reflector检查IEnumerable<T>源代码时,没有新的关键字,那么为什么BCL团队没有使用“new”关键字呢? - Mouhong Lin
1
Reflector不显示new关键字。它仅用于C#编译。BCL的那部分可能根本就不是用C#编写的。请记住,编译器警告只是来自C#,而不是来自.NET Framework。尝试编译我上面展示的第二个代码块。当您在Reflector中打开它时,“new”关键字将消失。 - David Morton

3
区别在于 IEnumerable<T>IEnumerable 是接口。在一个具体类型中,实现这两个接口之一必须显式地进行实现。
在具体类型继承中,您必须使用 new 关键字表示要覆盖基类型中的方法。当应用 new 关键字时,该方法不会被继承。这意味着 myTypeInstance.Method 调用定义在 MyType 上的方法,而 ((MyBaseType) myTypeInstance).Method 将调用定义在基类型上的方法。
public interface Interface1 {
    object GetItem();
}

public interface Interface2<T> : Interface1 {
    T GetItem();
}

public class MyBaseType : Interface1 {
    public object GetItem() {
        // implements Interface1.GetType()
        return null;
    }
}

public class MyType<T> : Interface1, Interface2<T> {
    public new T GetItem() {
        // Implements Interface2<T>.GetItem()
        return default(T);
    }

    object Interface1.GetItem() {
        // implements Interface1.GetItem()
        return this.GetItem();   // calls GetItem on MyType
    }
}

var myType = new MyType();
var myTypeResult = myType.GetItem(); // calls GetItem on MyType
var myBaseTypeResult = new ((MyBaseType) myType).GetItem(); // calls GetItem on MyBaseType

嗨,在我的场景中,Interface2<T>继承自Interface1,然后你会收到编译器警告。 - Mouhong Lin
只要其中一个 GetItem 方法明确地实现了,即使用 Interface.GetItem,就不应该有任何区别。 - AxelEckenberger
嗨,除非我使用 "new" 关键字,否则会有编译器警告。请查看我在帖子中的补充。 :) - Mouhong Lin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接