首先,它将有助于了解您请求的功能的名称:泛型类型协方差。C#支持以下情况下的协方差:
-
变化的类型参数是“两边”上的引用类型。也就是说,如果我们试图使用
IEnumerable<Tiger>
在这种情况下
IEnumerable<Animal>
是预期的,那么两者
Tiger
和
Animal
必须是引用类型。
-
泛型类型必须是接口或委托类型。
-
泛型类型必须由该类型的作者声明为协变(或逆变),并且C编译器必须能够
证明
类型是安全的。
你已经满足了使用方差的第一个条件,但是没有其他必要条件,所以你不能使用它。
另一种看法是:如果C允许你做你想做的事,会出什么问题?我们换衣服吧
Request<T>
到
Cage<T>
和
BaseCommand
到
动物
,只是为了让关系更清晰:
abstract class Animal {}
class Tiger : Animal {}
class Giraffe : Animal {}
class Cage<T> where T : Animal
{
public T Contents { get; set; }
}
好吧,现在让我们看看出了什么问题:
List<Cage<Animal>> list = new List<Cage<Animal>>(); // Clearly this must be legal
list.Add(new Cage<Tiger>); // This is the step that you want to be legal.
Cage<Animal> cage = list[0]; // Clearly this must be legal; list[0] is a list element.
cage.Contents = new Giraffe(); // Clearly this is legal; a giraffe is an animal.
这个程序片段有四行,其中三行必须是合法的,结果是一个类型错误:现在老虎笼子里有一只长颈鹿。因此,第二行必须是非法的,以防止类型错误。
你可以通过制作
Request
对协方差安全的接口:
interface IRequest<out T> where T : BaseCommand
// out T means make this type covariant in T
{
T Command { get; }
// T must appear in only *output* positions.
// That is, no properties of type T with setters,
// no methods that take a T as an input, and so on.
}
class Request<T> : IRequest<T> where T : BaseCommand
{ implement your class here }
...
var list = new List<IRequest<BaseCommand>>();
list.Add(new Request<CommandA>(new CommandA()));
现在没问题了。
Request<CommandA>
可转换为
IRequest<CommandA>
,它可以协变地转换为
IRequest<BaseCommand>
可以在列表中找到。
不像
笼子<T>
,
IRequest<T>
没有T型的长颈鹿,所以再也没有办法把长颈鹿放进老虎笼子里了,所以这是安全的。