IList 接口需要一个 Add 方法。数组实现了这个函数,但它只是抛出一个 NotImplementedException。这对我来说似乎是非常糟糕的设计。
当他们这样做时,设计师们在想什么?
ILists 可以是只读的 - 如果有疑问,调用者可以在尝试添加或删除元素之前测试 IsFixedSize 属性,或者在尝试修改元素之前测试 IsReadOnly 属性。
数组是固定大小的 IList。
能够将数组视为列表会很方便。一个示例是模拟返回 IList 的数据访问方法 - 可以模拟它以简单地将数组转换为 IList。
因为不同的对象通常具有不同的能力,所以一些接口包含将由一些但不是所有实现实现的成员。如果假设接口的某些(但不是全部)实现IVehicle
能够附加预告片,则典型的模式是将函数定义AttachTrailer
为“尝试附加预告片,如果CanAttachTrailer
为真,否则抛出NotSupportedException
”。的所有实现IVehicle
都将能够符合上述规范,无论它们是否可以处理拖车。
这种方法的一个优点是接口的实现可以提供许多不同的特性组合,而不必为不同的组合定义不同的类型。这种方法的另一个优点是Foo
,接收包含Foo
不需要的功能的对象的方法可以将该对象传递给Bar
确实需要这些功能的方法,而无需在任何地方进行任何类型转换,也不Foo
知道什么能力Bar
将需要。另一个优点是,这使得编写不需要某些功能但可以在它们存在时利用它们的代码变得容易。
然而,这种方法有一些缺点。接口还没有办法为任何属性或方法指定默认实现。因此,即使IVehicle
是无法附加预告片的实现也需要包含返回属性的代码false
,并在其方法CanAttachTrailer
中抛出异常。AttachTrailer
此外,由于接口不要求实现其许多方法,因此编译器无法知道拒绝尝试调用需要具有无法提供它的类型的对象的能力的函数。
在设计时IList<T>
,微软显然认为“可选功能接口”方法的优点超过了缺点。事实上,如果 .net 为实现接口的类提供了一种方法,以遵循它们不希望提供的成员的默认实现,那么几乎没有理由不在基础级接口中包含许多可选功能。为了允许在编译时强制执行必要的功能,可以从包含所有必需成员的基础派生许多接口,并指定实现后一个接口的类必须以实际有用的方式实现某些成员。例如,Microsoft 可以定义IResizableList<T>
为在不添加任何成员的情况下继承IList<T>
,但期望IList<T>
允许调整大小的实现将实现后一个接口,而不允许调整大小的实现不会实现它。如果他们这样做了,需要能够调整列表大小的代码可能需要一个IResizableList<T>
(在这种情况下它不会接受一个数组),而不需要调整列表大小的代码可能需要一个IList<T>
)。不幸的是,微软没有做任何类似的事情,所以代码不可能在编译时要求一个可调整大小的列表——如果传入的列表报告自己是固定大小的,它所能做的就是尖叫。