如果要求List <IFoo>,为什么我不能返回List <Foo>?

Joe*_* Gö 7 c# generics interface

我的理解是,如果S是一个子类的T,那么List<S>不是一个孩子List<T>.精细.但是接口有不同的范例:如果是Foo实现IFoo,那么为什么List<Foo>不是(一个例子)List<IFoo>

因为可能没有实际的类IFoo,这是否意味着我在暴露a时总是必须强制转换列表中的每个元素List<IFoo>?或者这只是糟糕的设计,我必须定义自己的集合类ListOfIFoos才能与它们一起工作?对我来说似乎都不合理......

考虑到我正在尝试编程接口,那么暴露这样一个列表的最佳方式是什么?我目前正倾向于实际存储我的List<Foo>内部作为List<IFoo>.

xto*_*ofl 14

List<Foo>不是子类,List<IFoo>因为您无法MyOwnFoo在其中存储对象,这也恰好是一个IFoo实现.(利斯科夫替代原则)

存储List<IFoo>而不是专用的想法List<Foo>是可以的.如果您需要将列表的内容强制转换为其实现类型,这可能意味着您的界面不合适.


Jon*_*eet 12

这是一个为什么你不能这样做的例子:

// Suppose we could do this...
public List<IDisposable> GetDisposables()
{
    return new List<MemoryStream>();
}

// Then we could do this
List<IDisposable> disposables = GetDisposables();
disposables.Add(new Form());
Run Code Online (Sandbox Code Playgroud)

此时,为了保存MemoryStreams而创建的列表现在有一个Form.坏!

基本上,这种限制是为了保持类型安全.在C#4和.NET 4.0中,对此的支持有限(称为方差),但由于上面给出的原因,它仍然不支持这种特定情况.


Tom*_*ter 8

在返回的函数中,您必须使列表成为接口列表,并在创建对象时将其作为实现它的对象.像这样:

function List<IFoo> getList()
{
  List<IFoo> r = new List<IFoo>();
  for(int i=0;i<100;i++)
      r.Add(new Foo(i+15));

  return r;
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*emp 5

大规模编辑 你将能够用C#4.0做到这一点,但 [感谢Jon]

你可以使用ConvertAll以下方法解决它:

public List<IFoo> IFoos()
{
    var x = new List<Foo>(); //Foo implements IFoo
    /* .. */
    return x.ConvertAll<IFoo>(f => f); //thanks Marc
}
Run Code Online (Sandbox Code Playgroud)