与C#Generics的协方差

Wil*_*ode 22 c# generics covariance

给定接口IQuestion和该接口的实现AMQuestion,假设以下示例:

List<AMQuestion> typed = new List<AMQuestion>();
IList<IQuestion> nonTyped = typed;
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,这个例子产生一个编译错误,说明两者的类型不同.但它表明存在明确的转换.所以我把它改成这样:

List<AMQuestion> typed = new List<AMQuestion>();
IList<IQuestion> nonTyped = typed as IList<IQuestion>;
Run Code Online (Sandbox Code Playgroud)

然后编译,但在运行时,nonTyped始终为空.如果有人可以解释两件事:

  • 为什么这不起作用.
  • 我怎样才能达到预期的效果.

这将不胜感激.谢谢!

Rot*_*tem 32

这样的事实AMQuestion实现了IQuestion接口并没有转化为List<AMQuestion>从派生List<IQuestion>.

因为此演员表是非法的,您的as运营商会返回null.

您必须单独投射每个项目:

IList<IQuestion> nonTyped = typed.Cast<IQuestion>().ToList();
Run Code Online (Sandbox Code Playgroud)

关于您的评论,请考虑以下代码,以及常见的陈词滥调动物示例:

//Lizard and Donkey inherit from Animal
List<Lizard> lizards = new List<Lizard> { new Lizard() };
List<Donkey> donkeys = new List<Donkey> { new Donkey() };

List<Animal> animals = lizards as List<Animal>; //let's pretend this doesn't return null
animals.Add(new Donkey()); //Reality unravels!
Run Code Online (Sandbox Code Playgroud)

如果我们被允许转换List<Lizard>为a List<Animal>,那么我们理论上可以Donkey在该列表中添加一个新的,这将破坏继承.

  • @WilliamCustode:只有你能保证类型安全.Rotem的编辑演示了一个可以传递不兼容类型的示例.但是,您可以"降级"某些内容:在这种情况下,您可以将`List <AMQuestion>`视为`IEnumerable <IQuestion>`.这是因为`IEnumerable <IQuestion>`没有提供任何机制来_change_它所以你总是保证类型安全.编辑:当您使用[`out`](http://msdn.microsoft.com/en-us/library/dd469487.aspx)和[`in`](http://msdn.microsoft)时,这会发挥作用.com/zh-cn/library/dd469484.aspx)关键字. (6认同)
  • 我更喜欢将它们视为*传统*而不是*陈词滥调*. (4认同)

Sne*_*tel 12

为什么它不工作:as返回null如果值的动态类型不能被强制转换为目标类型,并且List<AMQuestion>不能被强制转换为IList<IQuestion>.

但为什么不能呢?好吧,检查一下:

List<AMQuestion> typed = new List<AMQuestion>();
IList<IQuestion> nonTyped = typed as IList<IQuestion>;
nonTyped.Add(new OTQuestion());
AMQuestion whaaaat = typed[0];
Run Code Online (Sandbox Code Playgroud)

IList<IQuestion>说"你可以添加任何IQuestion给我".但这是一个它无法保留的承诺,如果它是一个List<AMQuestion>.

现在,如果您不想添加任何内容,只需将其视为IQuestion兼容事物的集合,那么最好的办法就是将其转换为IReadOnlyList<IQuestion>with List.AsReadOnly.由于只读列表不能添加奇怪的内容,因此可以正确地进行转换.


chi*_*oro 7

问题是List<AMQuestion>无法强制转换IList<IQuestion>,因此使用as运算符无济于事.在这种情况下显式转换意味着转换AMQuestionIQuestion:

IList<IQuestion> nonTyped = typed.Cast<IQuestion>.ToList();
Run Code Online (Sandbox Code Playgroud)

顺便说一句,你的标题中有"协方差"一词.在IList类型协变.这正是演员阵容不存在的原因.原因是IList界面具有T一些参数一些返回值,因此既in不能out也不能使用T.(@Sneftel有一个很好的例子来说明为什么不允许这种演员表.)

如果您只需要从列表中读取,则可以使用IEnumerable:

IEnumerable<IQuestion> = typed;
Run Code Online (Sandbox Code Playgroud)

这将起作用,因为IEnumerable<out T>out定义,因为您无法将其T作为参数传递.您通常应该在代码中使最弱的"承诺"保持可扩展性.