将对象添加到基类列表

时间:2018-11-28 13:59:11

标签: c#

具有基类:

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>());

任何人都可以解释这种行为,我们如何解决?   预先感谢

1 个答案:

答案 0 :(得分:9)

首先,它会帮助您知道您所请求的功能具有一个名称:通用类型协方差。在以下情况下,C#支持协方差:

  • 变化的类型参数是“两面”的引用类型。也就是说,如果我们在预期IEnumerable<Tiger>的情况下尝试使用IEnumerable<Animal>,则TigerAnimal都必须是引用类型。
  • 通用类型必须是接口或委托类型。
  • 该类型的创建者必须已将该通用类型声明为协变(或逆变),并且C#编译器必须能够证明该类型对于变异是安全的。

您已经满足使用方差的第一个条件,但没有其他必要条件,因此您不能使用它。

另一种看待它的方法是:如果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的二传手,因此不再有办法将长颈鹿放进老虎笼中,因此很安全。