PowerShell中如何扩展集合类并覆写Add方法
背景
我在PowerShell中有一个包含4个属性的数据对象,其中3个为字符串类型,第4个为Hashtable类型。我希望定义一种新类型,作为该数据对象的集合。
在这个集合类中,我希望强制执行特定格式,以便模块中的其他代码更易用。具体来说,我希望覆写Add方法,使得当3个字符串属性的组合唯一时,添加第4个哈希表属性;而当组合重复时,仅用新输入的哈希表更新已有行的哈希表属性。
这样可以抽象集合的扩展逻辑,确保调用Add方法时,集合始终保持按3个字符串属性的唯一组合分组哈希表的格式。
我的思路是创建一个继承自集合的类,然后覆写Add方法。
现有代码
以下代码包含3个类:
- 一个基于3个字符串属性的命名空间数据类(可在脚本中复用)。
- 一个为该数据类添加id属性的类,id是哈希表的键,对应的值为对象命名空间中的配置参数。
- 一个处理上述对象集合的类,用于定义Add方法。这是我遇到问题的地方。
Using namespace System.Collections.Generic Class Model_Namespace { [string]$Unit [string]$Date [string]$Name Model_Namespace([string]$unit, [string]$date, [string]$name) { $this.Unit = $unit $this.Date = $date $this.Name = $name } } Class Model_Config { [Model_Namespace]$namespace [Hashtable]$id Model_Config([Model_Namespace]$namespace, [hashtable]$config) { $this.namespace = $namespace $this.id = $config } Model_Config([string]$unit, [string]$date, [string]$name, [hashtable]$config) { $this.namespace = [Model_Namespace]::new($unit, $date, $name) $this.id = $config } } Class Collection_Configs { $List = [List[Model_Config]]@() [void] Add ([Model_Config]$newConfig ){ $checkNamespaceExists = $null $u = $newConfig.Unit $d = $newConfig.Date $n = $newConfig.Name $id = $newConfig.id $checkNamespaceExists = $this.List | Where { $u -eq $_.Unit -and $d -eq $_.Date -and $n -eq $_.Name } If ($checkNamespaceExists){ ($this.List | Where { $u -eq $_.Unit -and $d -eq $_.Date -and $n -eq $_.Name }).id += $id } Else { $this.List.add($newConfig) } } }
问题
我希望Collection_Configs类继承自内置集合类型并覆写Add方法。像泛型List<>类型一样,直接输出集合变量即可返回集合,无需通过List属性访问集合,甚至不需要List属性。
但继承System.Array时需要在构造函数中指定固定数组大小,而我的集合需要是可变的,因此希望避免这种方式。我尝试继承List,但语法无法生效,PowerShell抛出类型未找到的错误。
是否有办法实现这一需求?
在mklement的帮助下,我修改了最后一个类:
Using namespace System.Collections.ObjectModel Class Collection_Configs : System.Collections.ObjectModel.Collection[Object]{ [void] Add ([Model_Config]$newConfig ){ $checkNamespaceExists = $null $newConfigParams = $newConfig.namespace $u = $newConfigParams.Unit $d = $newConfigParams.Date $n = $newConfigParams.Name $id = $newConfig.id $checkNamespaceExists = $this.namespace | Where { $u -eq $_.Unit -and $d -eq $_.Date -and $n -eq $_.Name } If ($checkNamespaceExists){ ($this | Where { $u -eq $_.namespace.Unit -and $d -eq $_.namespace.Date -and $n -eq $_.namespace.Name }).id += $id } Else { ([Collection[object]]$this).add($newConfig) } } }
该代码似乎可以运行。除了继承外,我还修正了访问输入类型的方式,并且需要在其他两个类之后单独加载集合类,同时在else语句中调用基类的Add方法。
接下来我需要进行其他验证,确保仅能输入Model_Config类型。目前自定义集合接受任何输入,即使我将Add方法的参数转换为Model_Config类型,例如:
$config = [model_config]::new('a','b','c',@{'h'='t'}) $collection = [Collection_Configs]::new() $collection.Add($config)
可以正常运行,但:
$collection.Add('test')
也能运行,这是不符合预期的。可能是因为没有正确覆写方法,而是调用了基类的重载?
现在所有功能都正常了。最后更新的类代码如下:
using namespace System.Collections.ObjectModel Class Collection_Configs : Collection[Model_Config]{ [void] Add ([Model_Config]$newConfig ){ $checkNamespaceExists = $null $namespace = $newConfig.namespace $u = $namespace.Unit $d = $namespace.Date $n = $namespace.Name $id = $newConfig.id $checkNamespaceExists = $this.namespace | Where { $u -eq $_.Unit -and $d -eq $_.Date -and $n -eq $_.Name } If ($checkNamespaceExists){ ($this | Where { $u -eq $_.namespace.Unit -and $d -eq $_.namespace.Date -and $n -eq $_.namespace.Name }).id += $id } Else { [Collection[Model_Config]].GetMethod('Add').Invoke($this, [Model_Config[]]$newConfig) } } }
注意,else语句中的....GetMethod('Add')...在Windows PowerShell中是必需的,这是mklement0的回答中提到的。如果使用PowerShell Core,则可以使用mklement0提供的语法(我已测试通过)。
mklement0还提到,类型需要单独加载。在命令行快速测试时,可以先输入Model_Namespace和Model_Config类并回车,再输入Collection_Configs类并回车。
综上,该方案可在PowerShell中创建带自定义方法的自定义集合类型。
内容的提问来源于stack exchange,提问作者Blaisem

