如何覆盖 PowerShell 中 Collection 对象的 "member enumeration" 行为

How to override the "member enumeration" behavior of Collection objects in PowerShell

想象一下,我创建了一个 C# class 扩展 Collection<T> 来为特定用例添加额外的功能。

public class MyCollection<T> : Collection<T> {}

现在,如果我将这个 class 导入到我的 PowerShell 脚本中,并实例化这样一个对象,从所述对象调用 T 对象的成员方法将执行一种“魔术” - 为集合中的每一项依次调用成员方法的幕后花絮。

请注意,在此示例中,我使用自己的 MyCollection[String] 而不是 Collection[String] 没有任何区别。

Add-Type -Path ".\MyCollection.cs"
$test = New-Object -TypeName MyCollection[String]
$test.Add("one")
$test.Add("two")
$test.Add("three")
$test.Length
# 3
# 3
# 5

此行为或多或少被称为“成员枚举”。到这里来解释我在说什么:https://devblogs.microsoft.com/powershell/new-v3-language-features/

因为它实际上不是 运行 ForEach-Object cmdlet(参考上面 link),我希望找到 实际上 是什么关于以及如何,如果可能的话,我可以覆盖该行为而无需进行复杂的解决方法(例如在 MyCollection class 中为对象 class 中的每个方法创建一个包装方法)。

例如:

public class MyCollection<T> : Collection<T> {
    public new void ForEach-Object(this Action action) {
        DoSomethingAsync(this, action);
    }
}

我故意使用上面的无效语法来帮助说明问题。我真的不知道这是否可以做到。

或者,如果有另一种方法可以实现同样的事情 而无需 执行我之前提到的解决方法,我也有兴趣听听这些想法。

最终,所有这些集合类型都将实现 IEnumerable 接口,该接口使用 GetEnumerator 方法迭代对象集合,无论是在 .NET foreach 在您的 PowerShell Foreach-Object cmdlet 中循环。

一些具体的 Collection 实现将允许您提供自己的 GetEnumerator 实现,而其他的则不允许。我想不出有什么能让它脱离我的脑海,但我知道 Collection<T> 不会。如果您需要覆盖此功能,您可以尝试使用 new 关键字隐藏 GetEnumerator,正如您在上面演示的那样,它应该可以正常工作。另一种选择是编写自己的集合 class 来实现 IEnumerable,但这听起来很痛苦。

您可以 not 阻止 PowerShell 的 member-access enumeration(同时不使您的集合不可枚举),因为它是. 成员访问运算符内置的行为。

  • 但是,您可以在访问成员时绕过需要额外的努力,即通过 intrinsic .psbase property:[1]

    ((Get-Item /), (Get-Item /)).Name         # Member(-access) enumeration
    ((Get-Item /), (Get-Item /)).psbase.Name  # NO enumeration, only the array's
                                              # own members.
    

但是,如果您引用集合对象本身存在的 属性,则集合级别 属性 使用,覆盖成员访问枚举。

也就是说,$test.Count - 假设 .Count 是你的集合类型的一个(实例)成员 - return 只是 3,元素的数量集合。

GitHub suggestion #7445 要求为成员访问枚举提供不同的语法(运算符),但只要 PowerShell 继续致力于向后兼容(可能永远如此),. 的行为就会不变。


[1] 请注意,.psbase 属性 仅 调解 仅限于类型原生成员的成员访问。 .psbase 的值本身是一个 [pscustomobject] 实例,其 ETS(扩展类型系统)名称为 System.Management.Automation.PSMemberSet