我有以下类/接口:
// 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?)
是否有人可以解释为什么这是不可能的?
行,让我们取代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
比你可以上执行的操作不同 IContainer<IAnimal>
的类型不兼容。
你想要的功能被称为通用接口协方差 ,它是由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/
要解决你的代码,只是改变
public interface IB<T> where T : IA { }
至
public interface IB<out T> where T : IA { }
这是不容易看到当你有空接口。 考虑一下你在界面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型错误的对象!