创建一个自定义类型转换(也能处理 $null)
(續自: Cast to custom class in PowerShell) 我想要构建一个自定义的类型转换,类似于:
class CheckBox {
[Nullable[Bool]]$NullableBool
CheckBox([Nullable[Bool]]$NullableBool) { $this.NullableBool = $NullableBool }
[string] ToString() {
return $(
switch ($this.NullableBool) {
$null { '[?]' }
$false { '[X]' }
$true { '[V]' }
}
)
}
}
这行代码可用,唯一不行的是解析一个完全忽略构造函数的 $null:
$a = $true; Write-Host "Processed: $([CheckBox]$a)" # returns: "Processed: [V]"
$a = $false; Write-Host "Processed: $([CheckBox]$a)" # returns: "Processed: [X]"
$a = $null; Write-Host "Processed: $([CheckBox]$a)" # returns: "Processed: ", expected: "Processed: [?]"
另请参见:#26977
如果可能的话,我在PowerShell中如何实现这一点(使用与 [int]$null 相似的语法,得到 0)?
解决方案
简要总结:
- 你只能通过构造函数实现你想要的效果,不能也靠类型转换来实现。
- 因此,你唯一的选项是:
```bash # Method syntax
# New-Object alternative (more awkward) (New-Object Checkbox @($null)).ToString() ```
Casts 在PowerShell中承担着双重职责:
- 类型转换,类似于C#,但灵活性更高。例如,这其中既包括从一种类型真正转换到另一种类型(例如
[int] 1.1),也包括在.NET方法重载解析的上下文中,将一个类型实现的特定接口进行强制转换(例如[System.Collections.IDictionary] @{ foo=42 };下文亦有说明)。 - 实例构造:与C#不同,转换语法在某些情形下可以根据可用的构造函数和转换参数构造指定类型的实例。
不过,类型转换 的转换具有优先权,[Checkbox] $null 是一个有效的“转换”:由于你自定义的 [Checkbox] 类型本质上是一个.NET的引用类型,$null 可以被强制转换为它,[1],该表达式的结果只是 $null。
关于这种转换的有用性:由于 $null 没有固有类型,类似于 [Checkbox] $null 的表达式可以在.NET方法调用中引导重载解析,也就是在多个重载之间清晰地调用到目标重载——下一节有一个示例。
若要对PowerShell中的转换工作有一个总体概览,请参阅 这个回答。
一个简短的示例:
这是一个示例类,包含一个静态的 ::Bar() 方法,它有两个重载,唯一参数的类型不同;这两个类型都是.NET的引用类型,因此可能是 $null:
class Foo {
static [string] Bar([regex] $regex) { return "[regex] overload" }
static [string] Bar([hashtable] $hashtable) { return "[hashtable] overload" }
}
现在尝试用 $null 作为参数调用静态 ::Bar() 方法的某个重载:为确保能不产生歧义地选择目标重载,必须使用一个转换:
# ERROR due to ambiguity:
# 'Multiple ambiguous overloads found for "Bar" and the argument count: "1".'
[Foo]::Bar($null)
# OK: Cast needed to select the desired overload.
# -> "[regex] overload"
[Foo]::Bar([regex] $null)
[1] PowerShell也允许将 $null 进行转换以选择值类型,也就是.NET的 原始类型,如 [int], [double], ...但这并不等同于调用带参数的构造函数(这些类型没有带参数的构造函数);相反,这些是内置、PowerShell特定的转换,将 $null 解释为(与类型相符的)0;例如,[int] $null 的结果是 0(作为一个 [int])。
请注意,这不支持非原始值类型;例如,[datetime] $null 将导致错误。