具有基类:
public class BaseCommand {
}
和类:
public class CommandA : BaseCommand {
}
public class CommandB : BaseCommand {
}
BaseCommand的列表:
IList<BaseCommand> _Requests = new
List<BaseCommand>()
添加到此列表可以添加任何继承自BaseCommand的对象
但如果存在另一个名为Request的类:
public Request<T> where T : BaseCommand {
}
及其列表:
IList<Request<BaseCommand>> _Requests = new
List<Request<BaseCommand>>();
我们不能添加
_Requests.Add(new Request<CommandA>());
任何人都可以解释这种行为,我们如何解决? 预先感谢
答案 0 :(得分:9)
首先,它会帮助您知道您所请求的功能具有一个名称:通用类型协方差。在以下情况下,C#支持协方差:
IEnumerable<Tiger>
的情况下尝试使用IEnumerable<Animal>
,则Tiger
和Animal
都必须是引用类型。您已经满足使用方差的第一个条件,但没有其他必要条件,因此您不能使用它。
另一种看待它的方法是:如果C#允许您做您想做的事情,那会出什么问题?让我们将Request<T>
更改为Cage<T>
,将BaseCommand
更改为Animal
,只是为了使关系更清晰:
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>
,可在列表中使用。
与Cage<T>
不同,IRequest<T>
没有类型T的二传手,因此不再有办法将长颈鹿放进老虎笼中,因此很安全。