[System.Array]实现[System.Collections.IList],后者有一个.Add()方法。
Array 实现了IList,这是一个还涵盖可调整大小集合的接口,可能令人惊讶 - 听起来有historical reasons for it[1]
.
在 C# 中,这个惊喜很难偶然发现,因为您需要显式转换为IList 或使用IList 类型的变量才能访问.Add() 方法.
相比之下,从版本 3 开始,PowerShell 甚至将一个类型的 显式 接口实现显示为给定类型实例的 直接成员。(显式接口实现是那些在其实现中显式引用接口的实现,例如IList.Add() 而不仅仅是.Add();显式接口实现不是实现类型的公共接口的直接部分,这就是C# 需要强制转换/接口类型的原因变量来访问它们)。
作为这种设计的副产品,在 PowerShell 中,.Add() 方法可以在System.Array 实例上直接调用,这样更容易出错因为你可能没有意识到你正在调用一个接口方法。在数组的情况下,IList.Add() 实现(正确地)抛出一个异常,指出Collection was of a fixed size;后者是 NotSupportedException 类型的例外,这就是实现接口的类型应如何报告不支持接口的部分。
有帮助的是 Get-Member cmdlet 甚至只是引用一个方法而不调用它 - 只需省略 () - 允许您检查一个方法以确定它是否是该类型的本机或接口实现:
PS> (1, 2).Add # Inspect the definition of a sample array's .Add() method
OverloadDefinitions
-------------------
int IList.Add(System.Object value)
如您所见,输出显示.Add() 方法属于Ilist 接口。
[1]选读:.NET 中关于可变性的集合相关接口
免责声明:这不是我的专业领域。如果我的解释不正确/可以改进,请告诉我们。
集合相关接口的层次结构的根是ICollection(非泛型,自v1 起)和ICollection<T>(泛型,自v2 起)。
(它们依次实现IEnumerable / IEnumerable<T>,其唯一成员是.GetEnumerator() 方法。)
虽然非泛型 ICollection 接口值得称赞的是没有对集合的可变性做出任何假设,但它的泛型对应物 (ICollection<T>) 不幸的是 - 它包含用于修改集合的方法(文档甚至将接口的目的声明为“操纵通用集合”(强调))。在非泛型 v1 世界中,同样的情况也发生了,只是低于一级:非泛型 IList 包含集合修改方法。
通过在这些接口中包含突变方法,即使是只读/固定大小列表/集合(那些元素的数量和顺序不能改变,但它们的元素值可能)和完全不可变列表/集合(那些也不允许更改其元素的值)被强制实现变异方法,同时通过NotSupportedException 异常表明不支持它们。
虽然只读集合实现自 v1.1 以来就已经存在(例如,ReadOnlyCollectionBase),但就接口而言,直到 .NET v4 才存在。 5 中引入了IReadOnlyCollection<T> 和IImmutableList<T>(后者以及System.Collections.Immutable namespace 中的所有类型,仅作为可下载的NuGet 包提供)。
但是,由于从(实现)其他接口派生的接口永远不能排除成员,IReadOnlyCollection<T> 和 IImmutableCollection<T> 都不能从 ICollection<T> 派生,因此必须直接从共享根派生可枚举,IEnumerable<T>。
同样,实现IReadOnlyCollection<T> 的更专业的接口(例如IReadOnlyList<T>)也因此无法实现IList<T> 和ICollection<T>。
从根本上说,从头开始会提供以下解决方案,它颠倒当前逻辑:
使用ICollection 和IList 的示例,我们将得到以下接口层次结构:
IEnumerable<T> # has only a .GetEnumerator() method
ICollection<T> # adds a .Count property (only)
IResizableCollection<T> # adds .Add/Clear/Remove() methods
IList<T> # adds read-only indexed access
IListMutableElements<T> # adds writeable indexed access
IResizableList<T> # must also implement IResizableCollection<T>
IResizableListMutableElements<T> # adds writeable indexed access
IImmutableList<T> # guarantees immutability
注意:上面的 cmets 中只提到了显着的方法/属性。
请注意,这些新的ICollection<T> 和IList<T> 接口将提供no 变异方法(没有.Add() 方法,...,没有可分配的索引)。
IImmutableList<T> 与 IList<T> 的不同之处在于保证完全不变(并且目前提供仅复制的突变方法)。
System.Array 可以安全且完整地实现IList<T>,而接口的消费者不必担心NotSupportedExceptions。