我有一些非常奇怪的行為,我想這與點采購有關,但我無法理解它。這是我所擁有的:
sourced.ps1包含兩個函式和一個類的腳本:
class MyData {
[string] $Name
}
function withClass() {
$initialData = @{
Name1 = "1";
Name2 = "2";
}
$list = New-Object Collections.Generic.List[MyData]
foreach ($item in $initialData.Keys) {
$d = [MyData]::new()
$d.Name = $item
$list.Add($d)
}
}
function withString() {
$initialData = @{
Name1 = "1";
Name2 = "2";
}
$list = New-Object Collections.Generic.List[string]
foreach ($item in $initialData.Keys) {
$list.Add($item)
}
}
我還有一個腳本caller.ps1,它點源上面的那個并呼叫函式:
$ErrorActionPreference = 'Stop'
. ".\sourced.ps1"
withClass
然后我caller.ps1通過.\caller.ps1在 shell(帶有 PS Core 的 Win 終端)中執行來呼叫。
下面是我無法解釋的行為:如果我打電話.\caller.ps1,然后.\sourced.ps1再caller.ps1一次,我得到的錯誤:
Line |
14 | $list.Add($d)
| ~~~~~~~~~~~~~
| Cannot find an overload for "Add" and the argument count: "1".
但是,如果我改為更改caller.ps1to callwithString函式,則無論我呼叫caller.ps1和sourced.ps1.
此外,如果我第一次呼叫caller.ps1與withString,然后改變它withClass,沒有任何錯誤。
我想使用模塊會更正確,但我首先對這種奇怪行為的原因感興趣。
uj5u.com熱心網友回復:
從 PowerShell 7.2.1 開始撰寫
一個給定的腳本檔案,既點源和直接執行(以任何順序,不管多久)創建后續版本中的
class在它的定義-這些都是不同的.NET型別,即使它們的結構是相同的。可以說,沒有充分的理由這樣做,而且這種行為可能是一個錯誤。這些版本具有相同的全名(
class在腳本的頂級作用域中創建的PowerShell定義只有一個名稱,沒有命名空間),但位于不同的動態(記憶體中)程式集中,這些程式集的不同之處在于其版本的最后一個組件數目,陰影彼此,和哪一個是效果取決于背景關系:- 點源此類腳本的其他腳本始終會看到新版本。
- 在腳本本身內部,無論它本身是直接執行還是點源執行:
- 在 PowerShell 代碼中,原始版本仍然有效。
- 內部二進制的cmdlet,值得注意的是
New-Object,在新版本生效。 - 如果您混合這兩種方式來訪問腳本內的類,可能會發生型別不匹配,這就是您的情況 - 請參閱下面的示例代碼。
雖然您可以通過持續使用
::new()或New-Object參考類在技??術上避免此類錯誤,但最好避免對包含class定義的腳本檔案執行直接執行和點源。
示例代碼:
將代碼保存到腳本檔案中,例如,
demo.ps1執行兩次。
- 首先,通過直接執行:
.\demo.ps1 - 然后,通過點采購:
. .\demo.ps1
- 首先,通過直接執行:
您看到的型別不匹配錯誤將在第二次執行期間發生。
- 注意:錯誤資訊 ,
Cannot find an overload for "Add" and the argument count: "1"有點晦澀;它試圖表達的是.Add()不能使用給定型別的引數呼叫該方法,因為它需要新版本的實體[MyData],而::new()創建原始版本的實體。
- 注意:錯誤資訊 ,
# demo.ps1
# Define a class
class MyData { }
# Use New-Object to instantiate a generic list based on that class.
# This passes the type name as a *string*, and instantiation of the
# type happens *inside the cmdlet*.
# On the second execution, this will use the *new* [MyData] version.
Write-Verbose -Verbose 'Constructing list via New-Object'
$list = New-Object System.Collections.Generic.List[MyData]
# Use ::new() to create an instance of [MyData]
# Even on the second execution this will use the *original* [MyData] version
$myDataInstance = [MyData]::new()
# Try to add the instance to the list.
# On the second execution this will *fail*, because the [MyData] used
# by the list and the one that $myDataInstance is an instance of differ.
$list.Add($myDataInstance)
請注意,如果您使用$myDataInstance = New-Object MyData,則型別不匹配將消失。
同樣,如果您堅持::new()并使用它來實體化串列,它也會消失:$list = [Collections.Generic.List[MyData]]::new()
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/389640.html
