由于我需要能够从数组中添加和删除项目,因此我需要将其转换为,ArrayList
而不是更常见的选项([string[]]
、[Array]
、$Var=@()
)。我还需要用数据预设它,但函数的调用者需要能够根据需要更改预设。块中的预设Param()
是必需的,因为另一个实体正在寻找预设数据。我尝试了几种变体,但这是最有意义的一种:
Function Test-Me{
Param
(
$Properties = [System.Collection.ArrayList]@(
"red",
"blue",
"green")
)
$Properties.GetType()
$Properties.Add("orange")
}
上面的内容很棒,只是一旦有人调用Test-Me -Properties "Purple","Yellow","Black"
该$Properties
变量,它就会变成标准Array
类型(即添加和删除方法不起作用)。
我尝试过改变声明预设值的方式。看来预填充的行为是将类型转换为常规数组。我认为这是因为我使用了@()
预设,所以我()
也尝试了。
这也不起作用:
Param
(
[System.Collection.ArrayList]
$Properties = @("red",
"blue",
"green")
)
我有一个解决方法,可以转换 Param 块之外的类型,它看起来像这样:
Function Test-Me{
Param
(
[String[]]
$Properties = @("red",
"blue",
"green")
)
if("Orange" -notin $Properties){
[System.Collections.ArrayList]$Properties = $Properties
$Properties.Add("orange")
}
}
我觉得我应该能够将其转换为ArrayList
参数块并用数据预设它,并返回相同的数据类型,但我搞不懂。如果有人搞懂了,或者找到了无法运行的文档,请回答。
答案1
显式参数类型转换
您发布的第二个示例(明确转换参数变量)是正确的做法:
Function Test-Me {
Param(
[System.Collections.ArrayList]
$Properties = @("red","blue","green")
)
Write-Host $Properties.GetType().FullName
if("Orange" -notin $Properties){
[void]$Properties.Add("orange")
}
$Properties
}
导致:
PS C:\> Test-Me
System.Collections.ArrayList
red
blue
green
orange
PS C:\> Test-Me -Properties "one","two","three"
System.Collections.ArrayList
one
two
three
orange
输出类型
然而,令我感到惊讶的一件事是,即使有该[OutputType]
属性,该函数也会输出一个常规数组(这实际上可能是错误的预期行为,请参阅下面的更新):
Function Test-Me {
[OutputType([System.Collections.ArrayList])]
Param(
[System.Collections.ArrayList]
$Properties = @("red","blue","green")
)
if("Orange" -notin $Properties){
[void]$Properties.Add("orange")
}
$Properties
}
仍然导致返回常规对象数组:
PS C:\> (Test-Me).GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
更新(带有简单的解决方法)
正如您的评论所示连接错误提交,PowerShell 故意枚举任何可枚举输出的项目,以便为管道提供一致的行为(从 Connect 上的评论者 Derp McDerp 那里偷来的伪 C#):
if (returned_value is IEnumerable) {
foreach (r in returned_value) {
yield r;
}
} else {
yield returned_value;
}
诀窍是将集合包装在单个项目数组中,从而使 PowerShell 将其作为单个项目进行管道传输(注意,
before $Properties
):
Function Test-Me {
[OutputType([System.Collections.ArrayList])]
Param(
[System.Collections.ArrayList]
$Properties = @("red","blue","green")
)
if("Orange" -notin $Properties){
[void]$Properties.Add("orange")
}
,$Properties
}
现在,我们得到了正确类型的输出:
PS C:\> (Test-Me).GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True ArrayList System.Object
答案2
一位同事刚刚要求我解释一些与此相关的细节,所以我认为与将来带着这个问题来这里的人分享一些补充信息可能会有所帮助。
在 PowerShell 中,有三种基本方法可以为变量赋值:
# Loosely typed
# Can be reassigned to anything without error
$myVariable1 = @('Red','Green','Blue')
# Type cast
# Still loosely typed
# Can still be reassigned to anything without issue
$myVariable2 = [System.Collections.ArrayList]@('Red','Green','Blue')
# Strongly typed
# Can only be assigned objects of that type or objects that implicitly convert into that type
[System.Collections.ArrayList]$myVariable3 = 'Red','Green','Blue'
如果要确保变量(或函数参数)属于特定类型,则必须使用强类型,就像 所做的那样$myVariable3
。这样做可以防止类型意外更改为其他类型(例如,$myVariable3 = 42
将因错误而失败)。
了解可以使用 cmdlet 识别变量是否为强类型很有用Get-Variable
。例如,调用Get-Variable myVariable1,myVariable2,myVariable3 | Format-List Name,Value,Visibility,Options,Attributes
会产生以下输出:
Name : myVariable1
Value : {Red, Green, Blue}
Visibility : Public
Options : None
Attributes : {}
Name : myVariable2
Value : {Red, Green, Blue}
Visibility : Public
Options : None
Attributes : {}
Name : myVariable3
Value : {Red, Green, Blue}
Visibility : Public
Options : None
Attributes : {System.Management.Automation.ArgumentTypeConverterAttribute}
请注意,除了myVariable3
变量在属性中有一个属性之外,它们都是相同的Attributes
。ArgumentTypeConverterAttribute
任何强类型的变量都会添加一个,该属性将确保变量保持相同的类型,无论您将其分配给什么。
了解变量赋值的工作原理,以及如何返回完整的集合,正如 Mathias 指出的那样多于,使得在 PowerShell 中处理数据变得更加容易。