lit*_*lit 4 arrays powershell interface
我完全理解System.Array是不可变的.
鉴于此,为什么它有一个Add()方法?
它没有出现在Get-Member的输出中.
$a = @('now', 'then')
$a.Add('whatever')
Run Code Online (Sandbox Code Playgroud)
是的,我知道这失败了,我知道它失败的原因.我不是要求使用[System.Collections.ArrayList]或建议[System.Collections.Generic.List[object]].
[System.Array]实现[System.Collections.IList],后者有一个.Add()方法.
即Array工具IList,其是还包括一个接口 可调整大小的集合,可以是令人惊讶的 -它听起来像有它历史原因[1]
.
在C#中,这个惊喜很难偶然发现,因为你需要显式地转换IList或使用一个IList-typed变量才能访问该.Add()方法.
相比之下,从版本3开始,PowerShell甚至将类型的显式接口实现表面化为给定类型实例的直接成员.(显式接口实现是那些在其实现中显式引用接口的实现,例如IList.Add()而不仅仅是.Add();显式接口实现不是实现类型的公共接口的直接部分,这就是为什么C#需要一个强制转换/接口类型的变量来访问它们).
作为此设计的副产品,在PowerShell中,.Add()可以直接在System.Array实例上调用该方法,这使得更容易发现问题,因为您可能没有意识到您正在调用接口方法.在数组的情况下,IList.Add()实现(正确地)抛出一个异常,说明Collection was of a fixed size; 后者是类型的一个例外NotSupportedException,它是实现接口的类型应该如何报告不支持接口的部分.
有用的是Get-Membercmdlet甚至只是在不调用它的情况下引用方法- 只需省略()- 允许您检查方法以确定它是类型的本机还是接口实现:
PS> (1, 2).Add # Inspect the definition of a sample array's .Add() method
OverloadDefinitions
-------------------
int IList.Add(System.Object value)
Run Code Online (Sandbox Code Playgroud)
如您所见,输出显示该.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命名空间,唯一可用作为可下载的NuGet包).
但是,由于从(实现)其他接口派生的接口永远不能排除成员,因此既不能IReadOnlyCollection<T>也IImmutableCollection<T>不能从中派生,ICollection<T>因此必须直接从枚举的共享根中派生出来IEnumerable<T>.同样,更专业的接口,例如IReadOnlyList<T>该工具IReadOnlyCollection<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
Run Code Online (Sandbox Code Playgroud)
注意:上面的注释中只提到了显着的方法/属性.
需要注意的是,这些新的ICollection<T>和IList<T>接口将提供没有突变的方法(没有.Add()方法,...,没有可分配的索引).
IImmutableList<T>IList<T>通过保证完全不变性而不同(并且,目前,提供仅限复制方法的突变).
System.Array然后可以安全地完全实现IList<T>,而界面的消费者不必担心NotSupportedExceptions.
要"添加"到@ mklement0的答案:[System.Array]实现指定方法的[System.Collections.IList]Add().
但要回答为什么有一个Add()如果它不起作用?好吧,我们没有看过其他属性,即IsFixedSize:
PS > $a = @('now', 'then')
PS > $a.IsFixedSize
True
Run Code Online (Sandbox Code Playgroud)
所以,a [System.Array]只是[System.Collections.IList]一个固定大小.当我们回顾该Add()方法时,它明确定义了如果List是只读或固定大小,则抛出NotSupportedException它所做的.
我认为本质不是,"让我们有一个函数只是无缘无故地抛出错误信息",或者扩展它,除了实现一个接口之外别无其他理由,但实际上它提供的警告是你合法地做的你不应该做的事情.
这是典型的界面思想,你可以有一个IAnimal类型,一个GetLeg()方法.这种方法将占所有动物的90%,这使得它成为实现到基础接口的一个很好的理由,但是当你对一个Snake对象使用它时会抛出一个错误, 因为你没有先检查.HasFeet属性.
该Add()方法对于List接口来说是一个非常好的方法,因为它是非Readonly和Non-Fixed长度列表的基本方法.我们是愚蠢的,因为IsFixedSize在调用Add()不起作用的方法之前不检查列表是不是.即$null在尝试使用之前,这属于检查类别.
| 归档时间: |
|
| 查看次数: |
162 次 |
| 最近记录: |