我正在設計一個模塊并使用類對我的引數進行型別驗證。我注意到,在嘗試對輸入引數進行型別驗證時,具有單引數建構式的類似乎充當了型別加速器,而不是驗證資料型別。
例子:
Class stack {
$a
$b
stack($inp) {
$this.a = $inp
$this.b = 'anything'
}
}
function foo {
Param(
[stack]$bar
)
$bar
}
PS>foo -bar 'hello'
a b
- -
hello anything
$bar 已被型別加速為堆疊的實體化。
將此與具有帶 2 個引數的建構式的同一類進行比較:
Class stack {
$a
$b
stack($inp,$inp2) {
$this.a = $inp
$this.b = 'anything'
}
}
function foo {
Param(
[stack]$bar
)
$bar
}
PS>foo -bar 'hello'
foo : Cannot process argument transformation on parameter 'bar'. Cannot convert the "hello" value of type "System.String" to type "stack".
現在型別別正在正確驗證輸入引數。
我第一次在 Windows 10 的 PS5.1 上看到了這個,但我只是在我的私人筆記本電腦上用 pwsh 7.2.1 試了一下,似乎是一樣的。
這種行為有解決方法嗎?它是一個錯誤嗎?
編輯:好吧,經過進一步測驗,我意識到如果我為帶有 2 個引數的建構式提供 2 個輸入引數,例如foo -bar 'hello' 'world'. 所以我想這可能是故意的,我做錯了什么。我可以使用類來驗證輸入引數的資料型別嗎?如何?
uj5u.com熱心網友回復:
您所看到的與型別加速器無關,它們只是.NET 型別名稱的短別名;例如,[regex]是 的縮寫[System.Text.RegularExpressions.Regex]。
相反,您會看到 PowerShell 靈活的自動型別轉換,其中包括將強制轉換(例如[stack] ...)和型別約束(同上,在賦值背景關系中或param(...)塊內)轉換為建構式呼叫或::Parse()呼叫,如本答案中所述。
- 因此,假設您的
[stack]類有一個(非型別約束)單引數建構式,類似的東西[stack] 'hello'會自動轉換為[stack]::new('hello'),即建構式呼叫 - 當您將引數'hello'傳遞給型別為的引數[stack]時也會發生這種情況。
我建議不要與這些自動轉換作斗爭,因為它們通常很有幫助。
在極少數情況下,您確實需要確保傳遞的引數的型別與引數宣告中指定的型別(或派生型別)完全一致,您可以使用以下技術(以 type[datetime]為例,其完整.NET 型別名稱是System.DateTime):
function Foo {
param(
# Ensure that whatever argument is passed is already of type [datetime]
[PSTypeName('System.DateTime')]
$Bar
)
"[$Bar]"
}
感謝您發現[PSTypeName()]此用例的屬性。
如果沒有該
[PSTypeName(...)]屬性,則可以進行諸如此類的呼叫Foo 1/1/1970,因為該字串 會由 PowerShell'1/1/1970'自動轉換為。[datetime]使用
[PSTypeName(...)]屬性,只接受一個實際[datetime]引數(或者,對于可以被子類化的型別,一個從指定型別派生的型別的實體)。重要提示:指定目標型別的完整.NET 型別名稱(例如
'System.DateTime',而不僅僅是'datetime')以明確地定位它。- 但是,對于PowerShell 自定義
classes,它們的名稱是全名(它們不在namespace內),因此對于您的[stack]類,屬性將是[PSTypeName('stack')]
- 但是,對于PowerShell 自定義
接受任何型別名稱,即使它不參考現有的 .NET 型別或自定義
class,并且任何此類不存在的型別都需要一個引數來使用匹配的虛擬 ETS(PowerShell 的擴展型別系統)型別名稱。事實上,支持這種虛擬型別名稱是這個屬性的主要目的。[1]例如,如果[PSTypeName('Bar')]使用,您可以傳遞一個具有如下ETS型別名稱的自定義物件:Bar
[pscustomobject] @{ PSTypeName = 'Bar'; Baz = 'quux' }
[1] 參考鏈接檔案(強調添加):“當型別超出 .NET 型別系統時,此屬性用于限制引數的型別名稱。”
uj5u.com熱心網友回復:
如果您真的想驗證傳遞的型別,您需要實際驗證,而不僅僅是將輸入轉換為特定型別。
function foo {
Param(
[ValidateScript({$_ -is [stack]})]
$bar
)
$bar
}
這樣做不會嘗試將輸入轉換為特定型別,如果輸入型別錯誤,則會失敗。
uj5u.com熱心網友回復:
你的 'foo' 函式需要一個 [stack] 引數,它不會創建一個。
所以你的電話應該如下:
foo -bar ([stack]::new('fun:foo','hello'))
我不知道你將如何使用它,但如果目標是驗證引數,我建議在任何地方指定型別......這是一個小例子,但它可以改進,只是一個例子:
Class stack {
[string]$a
[string]$b
stack([string]$inp,[string]$inp2) {
$this.a = $inp
$this.b = $inp2
}
}
function foo {
Param([stack]$bar)
$bar
}
function foo2 {
Param([array]$bar)
[stack]::new($bar[0],$bar[1])
}
foo -bar ([stack]::new('fun:foo','hello'))
foo2 -bar 'fun:foo2','hello'
foo2 -bar @('fun:foo2','hello2')
uj5u.com熱心網友回復:
啊哈,我以為我在某處看到過這種效果。幸運的是,我通過將一篇關于使用 PSCustomObjects 中的 PSTypeName 對類進行型別驗證的文章中的描述應用到它的作業原理。事實證明,類也使用相同的語法。
總之,似乎必須鍵入 [PSTypeName('stack')] 才能使用型別別來驗證資料型別。
Class stack {
$a
$b
stack($inp) {
$this.a = $inp
$this.b = 'anything'
}
}
function foo {
Param(
[PSTypeName('stack')]$bar
)
$bar
}
PS>foo -bar 'hello'
foo : Cannot bind argument to parameter 'bar', because PSTypeNames of the argument do not match the PSTypeName required by the parameter: stack.
PS>$test = [stack]::new('Overflow')
PS>foo -bar $test
a b
- -
Overflow anything
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/412551.html
標籤:
上一篇:從特定AD組匯出AD成員
