我有以下类/接口:
// Model
public class A : IA { }
// ModelLogic
public class B : IB<A> { }
// Model Interface
public interface IA { }
// ModelLogic Interface
public interface IB<T> where T : IA { }
我尝试使用以下代码创建一个新实例:
IB<IA> foo = new B();
我收到以下错误:
Cannot implicitly convert type 'B' to 'IB<IA>'. An explicit conversion exists (are you missing a cast?)
有人可以解释为什么这是不可能的吗?
答案 0 :(得分:43)
好的,我们将A
替换为Fish
,将IA
替换为IAnimal
,将B
替换为Aquarium
,将IB<T>
替换为IContainer<T>
IContainer<T>
。我们会将成员添加到IAnimal
,并将第二个实现添加到// Model
public class Fish : IAnimal { }
public class Tiger : IAnimal { }
// ModelLogic
public class Aquarium : IContainer<Fish>
{
public Fish Contents { get; set; }
}
// Model Interface
public interface IAnimal { }
// ModelLogic Interface
public interface IContainer<T> where T : IAnimal
{
T Contents { get; set; }
}
IContainer<IAnimal> foo = new Aquarium(); // Why is this illegal?
foo.Contents = new Tiger(); // Because this is legal!
:
Aquarium
你可以将老虎放入foo - foo被输入为可以包含任何动物的容器。但你只能把鱼放入水族馆。由于您在IContainer<IAnimal>
上合法执行的操作与<{1}}上可执行的操作不同,因此类型不兼容。
您想要的功能称为通用接口协方差,C#4支持 ,但您必须向编译器证明您永远不会让老虎进入你的鱼缸。你想要做的是:
// Model
public class A : IA { }
// ModelLogic
public class B : IB<A> { }
// Model Interface
public interface IA { }
// ModelLogic Interface
public interface IB<out T> where T : IA { }
注意IB
上的协方差注释。此out
表示 T
只能用作输出,而不能用作输入。如果T
只是一个输出,那么就没有人可以将老虎放入该鱼缸,因为没有“投入”财产或方法。
我在将这个功能添加到C#时写了一些博客文章;如果您对该功能的设计注意事项感兴趣,请参阅:
http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/
答案 1 :(得分:1)
要修复您的代码,只需更改
即可public interface IB<T> where T : IA { }
到
public interface IB<out T> where T : IA { }
答案 2 :(得分:0)
当你有空接口时,不容易看到。考虑在接口IB中有一个方法M:
public interface IB<T> where T : IA
{
void M(T t);
}
这是B的实现:
public class B : IB<A>
{
public void M(A t)
{
// only object of type A accepted
}
}
然后你有对象C,它也实现了IA:
public class C : IA { }
因此,如果您的代码可以使用,那么您可以调用:
IB<IA> foo = new B();
foo.M(new C());
问题是B类只接受A类型的对象。错误!