如果要求List ,为什么我不能返回List ?
我理解,如果S
是T
的子类,则List
不是 List
。 精细。 但是接口有不同的范例:如果Foo
实现了IFoo
,那么为什么List
不是(一个例子) List
?
因为可能没有实际的类IFoo
,这是否意味着我在暴露List
时总是必须List
列表中的每个元素? 或者这只是糟糕的设计,我必须定义我自己的集合类ListOfIFoos
才能使用它们? 对我来说似乎都不合理……
考虑到我正在尝试编程接口,那么暴露这样一个列表的最佳方式是什么? 我目前正倾向于将List
内部存储为List
。
如果List
您的List
不是子类,因为您无法在其中存储MyOwnFoo
对象,这也恰好是IFoo
实现。 ( 利斯科夫替代原则 )
存储List
而不是专用List
的想法是可以的。 如果您需要将列表的内容强制转换为它的实现类型,这可能意味着您的接口不合适。
这是一个为什么你不能这样做的例子:
// Suppose we could do this... public List GetDisposables() { return new List(); } // Then we could do this List disposables = GetDisposables(); disposables.Add(new Form());
此时,为了保存MemoryStreams而创建的列表现在包含一个Form。 坏!
基本上,这种限制是为了保持类型安全。 在C#4和.NET 4.0中,对此的支持有限 (称为方差 ),但由于上面给出的原因,它仍然不支持这种特定情况。
在返回的函数中,您必须使列表成为接口列表,并在创建对象时将其作为实现它的对象。 像这样:
function List
getList() { List r = new List (); for(int i=0;i<100;i++) r.Add(new Foo(i+15)); return r; }
大规模编辑 你将能够用C#4.0做到这一点,但是 [感谢Jon]
你可以使用ConvertAll
绕过它:
public List IFoos() { var x = new List(); //Foo implements IFoo /* .. */ return x.ConvertAll(f => f); //thanks Marc }
简单的答案是List
是List
的不同类型,例如,与DateTime
与IPAddress
不同的方式相同。
但是,你有IFoo
的事实意味着IFoo
集合应该包含至少两个IFoo
实现( FooA
, FooB
等等),因为如果你期望只有IFoo
一个实现, Foo
,那么IFoo
类型是多余的。
因此,如果只有一个派生类型的接口,请忘记接口并节省开销。 如果有两个或多个派生类型的接口,则始终在collections / generic参数中使用接口类型。
如果你发现自己编写了thunking代码,那么某个地方可能存在设计缺陷。
如果在发明IList
时,Microsft已经意识到.net的未来版本将支持接口协方差和逆变,那么将接口拆分为IReadableList
, IAppendable
将是可能且有用的。 IAppendable
, IList
将inheritance以上两者。 这样做会对vb.net实现者施加少量额外的工作(他们必须定义索引属性的只读和读写版本,因为由于某种原因.net不允许读写要作为只读属性的属性)但是意味着只需要从列表中读取项目的方法可以以协变方式接收IReadableList
,并且只需要他们可以追加的集合的方法可以接收IAppendable
以逆变的方式。
不幸的是,今天可以实现这种事情的唯一方法是,如果微软提供了一种新接口可以替代旧接口的方法,旧接口的实现会自动使用新接口提供的默认方法。 我认为这样的function(界面可替代性)会非常有用,但我不会屏住呼吸等待微软实施它。
鉴于没有办法将IReadableList
到IList
,另一种方法是定义一个自己的列表相关接口。 这样做的一个难点是System.Collections.Generic.List
所有实例都必须替换为其他类型,尽管如果要定义List
,可以最大限度地减少这样做的难度。 struct包含在一个不同的命名空间中,该命名空间包含一个System.Collections.Generic.List
字段,并定义了与系统类型之间的扩展转换(使用结构而不是类)意味着代码将避免创建新堆的需要在任何不必加框结构的场景中进行转换时的对象)。