如果我有一个带有协变类型参数的通用接口,就像这样:
interface IGeneric<out T>
{
string GetName();
}
如果我定义了这个类层次结构:
class Base {}
class Derived1 : Base{}
class Derived2 : Base{}
然后我可以在一个类上实现接口两次,像这样使用显式接口实现:
class DoubleDown: IGeneric<Derived1>, IGeneric<Derived2>
{
string IGeneric<Derived1>.GetName()
{
return "Derived1";
}
string IGeneric<Derived2>.GetName()
{
return "Derived2";
}
}
如果我使用(非泛型)DoubleDown
类并将其转换为IGeneric<Derived1>
或IGeneric<Derived2>
,则它会正常工作:
var x = new DoubleDown();
IGeneric<Derived1> id1 = x; //cast to IGeneric<Derived1>
Console.WriteLine(id1.GetName()); //Derived1
IGeneric<Derived2> id2 = x; //cast to IGeneric<Derived2>
Console.WriteLine(id2.GetName()); //Derived2
然而,将
x
转换为IGeneric<Base>
,会得到以下结果:IGeneric<Base> b = x;
Console.WriteLine(b.GetName()); //Derived1
我原本期望编译器会发出错误提示,因为这个调用在两个实现间不明确,但是它返回了第一个声明的接口。
为什么允许这样的情况呢?
(灵感来源于一个类实现两个不同的IObservables?。我试图向同事展示这将失败,但不知何故,它没有失败)
Console.WriteLine(b.GetName());
,编译器 无法发出任何错误;它有一个 IGeneric<Base> 来调用 getName,这是完全有效的调用。 - Miserable Variableb
的 静态 类型,即IGeneric<Base>
,在该类型上调用GetName
是有效的。如果您认为错误应该出现在DoubleDown
中,那么这不是一个错误,因为有一个明确定义的规则,即匹配是未指定的。 - Miserable VariableIEnumerable<>
是协变的,并创建了一个既是IEnumerable<Giraffe>
又是IEnumerable<Turtle>
的类C
。然后,通过协变,该类的实例就成为了IEnumerable<Animal>
。因此,存在相同的歧义。 - Jeppe Stig Nielsen