在这个函数中,有两个奇怪的行为(在 Windows PS 5.1/7.4.6 上测试):
- 验证脚本运行多次
$FilePath
永远不会转换为 FileInfo 对象,而是保留为字符串
function Test-Param {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateScript({
Write-Host "Test $((Get-Date).Ticks)"
[IO.File]::Exists( (Resolve-Path -Path $_).Path )
})]
[string] $FilePath
)
Start-Sleep -Milliseconds 50
Write-Host "Start"
$FilePath = New-Object IO.FileInfo (Resolve-Path -Path $FilePath).Path
Write-Host "After expected object type change"
$FilePath.GetType().FullName
$FilePath
}
Test-Param -FilePath $MyInvocation.MyCommand.Path
输出如下:
Test 638746054867610941
Start
Test 638746054868236274
After expected object type change
System.String
C:\test\test.ps1
这似乎不应该发生,我希望参数验证只运行一次,并且 FilePath 变量将被不同类型的对象覆盖。原本想在 powershell github 上发帖,但想先征求 SO 社区的意见... 这是设计使然还是潜在的错误?
更新 - 正如@theo 在评论中指出的那样,如果参数类型被删除或更改为[object]
FilePath 对象类型将会是预期的 FileInfo 对象。
(参数)变量实现
类型约束(例如,放置
[string]
在参数变量声明之前)属性修饰(例如,将
[ValidateScript({...})]
属性放在参数变量声明之前)作为附加到变量对象的属性(
(Get-Variable FilePath).Attributes | Format-List
从函数体中验证),每次将变量分配给时都会对其进行评估。[1]以上解释了您观察到的所有行为:
因为您已将类型限制
$FilePath
为[string]
,所以相应的属性也会将您稍后分配的任何值转换为[string]
,从而有效地防止类型改变。因为您在函数主体中
$FilePath
再次分配($FilePath = New-Object IO.FileInfo ...
),所以[ValidateScript()]
属性的脚本块被再次调用。您可以按如下方式绕过(可能令人惊讶的)行为:
将参数声明变量视为只读- 这样,在参数绑定期间,与它们相关的任何约束和验证都只会评估一次。
在函数或脚本主体中使用具有不同名称的单独的常规变量来对参数值执行转换;例如,在您的情况下:
[1] 请注意,这也适用于常规变量,即在函数主体
param(...)
中创建的变量,而不是在块内定义参数的变量。虽然对常规变量的类型约束并不罕见(例如,
[int] $i = 42
;请参阅此答案),但验证属性却很少见(例如,[ValidateRange(1, 41)] [int] $i = 41
;稍后尝试分配该范围之外的值,例如$i = 42
,然后导致(语句终止)错误)。