(版本3)显然,在PowerShell中不是所有的$null
的是相同的:
>function emptyArray() { @() }
>$l_t = @() ; $l_t.Count
0
>$l_t1 = @(); $l_t1 -eq $null; $l_t1.count; $l_t1.gettype()
0
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
>$l_t += $l_t1; $l_t.Count
0
>$l_t += emptyArray; $l_t.Count
0
>$l_t2 = emptyArray; $l_t2 -eq $null; $l_t2.Count; $l_t2.gettype()
True
0
You cannot call a method on a null-valued expression.
At line:1 char:38
+ $l_t2 = emptyArray; $l_t2 -eq $null; $l_t2.Count; $l_t2.gettype()
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
>$l_t += $l_t2; $l_t.Count
0
>$l_t3 = $null; $l_t3 -eq $null;$l_t3.gettype()
True
You cannot call a method on a null-valued expression.
At line:1 char:32
+ $l_t3 = $null; $l_t3 -eq $null;$l_t3.gettype()
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
>$l_t += $l_t3; $l_t.count
1
>function addToArray($l_a, $l_b) { $l_a += $l_b; $l_a.count }
>$l_t = @(); $l_t.Count
0
>addToArray $l_t $l_t1
0
>addToArray $l_t $l_t2
1
因此,如何以及为什么是$l_t2
从不同$l_t3
? 尤其是$l_t2
真的$null
或不? 需要注意的是$l_t2
不是一个空数组( $l_t1
是,和$l_t1 -eq $null
返回任何结果,如预期),但也不是真正的$null
,像$l_t3
。 特别是, $l_t2.count
返回0而不是错误,此外,加入$l_t2
到$l_t
行为类似于添加一个空的阵列,而不是像添加$null
。 又为何$l_t2
似乎突然变得“更$null
当它在这个函数会得到” addToArray
作为参数???????
任何人都可以解释这种行为,或指向我的文档,将解释呢?
编辑:下面由PetSerAl的答案是正确的。 我也发现了同样的问题,这个计算器职位。
Powershell的版本信息:
>$PSVersionTable
Name Value
---- -----
WSManStackVersion 3.0
PSCompatibleVersions {1.0, 2.0, 3.0}
SerializationVersion 1.1.0.1
BuildVersion 6.2.9200.16481
PSVersion 3.0
CLRVersion 4.0.30319.1026
PSRemotingProtocolVersion 2.2
尤其是$l_t2
真的$null
或不?
$l_t2
不是$null
,但[System.Management.Automation.Internal.AutomationNull]::Value
。 这是一个特殊的例子PSObject
。 当管道返回零个对象则返回。 这是你可以检查一下:
$a=&{} #shortest, I know, pipeline, that returns zero objects
$b=[System.Management.Automation.Internal.AutomationNull]::Value
$ReferenceEquals=[Object].GetMethod('ReferenceEquals')
$ReferenceEquals.Invoke($null,($a,$null)) #returns False
$ReferenceEquals.Invoke($null,($a,$b)) #returns True
我打电话ReferenceEquals
通反射,从而防止转换从AutomationNull
为$ null通过PowerShell的。
$l_t1 -eq $null
返回任何内容
对我来说,它返回一个空数组,当我从它的期望。
$l_t2.count
返回0
这是一个PowerShell的V3的新功能 :
现在,您可以使用数量或长度的任何对象,即使它没有足够的财产。 如果对象没有一个计数或长度属性,则返回1(或0 $空)。 具有计数或长度属性的对象将继续工作,因为他们总是有。
PS> $a = 42 PS> $a.Count 1
又为何$l_t2
似乎突然变得“更$null
当它在这个函数会得到” addToArray
作为参数???????
看来,PowerShell的转换AutomationNull
到$null
在某些情况下,比如调用.NET方法。 在PowerShell中V2,节约即使AutomationNull
给一个变量它被转换为$null
。
为了补充 PetSerAl最伟大的答案用务实的总结 :
注意事项 :
传递[System.Management.Automation.Internal.AutomationNull]::Value
为cmdlet /函数的参数值总是将其转换为$null
。
在PSv3 +,甚至是实际的(标量) $null
是不是在列举foreach
循环 ; 它列举了管道,但是-见底部。
在PSv2-, 节省了空数组变量中悄然转换它为$null
和$null
在一个枚举foreach
循环以及 (不只是在流水线) -见底部。
# A true $null value:
$v1 = $null
# An operation with no output returns
# the [System.Management.Automation.Internal.AutomationNull]::Value singleton,
# which is treated like $null in a scalar expression context,
# but behaves like an empty array in a pipeline or array expression context.
$v2 = & {} # calling (&) an empty script block ({}) produces no output
# In a *scalar expression*, [System.Management.Automation.Internal.AutomationNull]::Value
# is implicitly converted to $null, which is why all of the following commands
# return $true.
$null -eq $v2
$v1 -eq $v2
$null -eq [System.Management.Automation.Internal.AutomationNull]::Value
& { param($param) $null -eq $param } $v2
# By contrast, in a *pipeline*, $null and
# [System.Management.Automation.Internal.AutomationNull]::Value
# are NOT the same:
# Actual $null *is* sent as data through the pipeline:
# The (implied) -Process block executes once.
$v1 | % { 'input received' } # -> 'input received'
# [System.Management.Automation.Internal.AutomationNull]::Value is *not* sent
# as data through the pipeline, it behaves like an empty array:
# The (implied) -Process block does *not* execute (but -Begin and -End blocks would).
$v2 | % { 'input received' } # -> NO output; effectively like: @() | % { 'input received' }
# Similarly, in an *array expression* context
# [System.Management.Automation.Internal.AutomationNull]::Value also behaves
# like an empty array:
(@() + $v2).Count # -> 0 - contrast with (@() + $v1).Count, which returns 1.
# CAVEAT: Passing [System.Management.Automation.Internal.AutomationNull]::Value to
# *any parameter* converts it to actual $null, whether that parameter is an
# array parameter or not.
# Passing [System.Management.Automation.Internal.AutomationNull]::Value is equivalent
# to passing true $null or omitting the parameter (by contrast,
# passing @() would result in an actual, empty array instance).
& { param([object[]] $param)
[Object].GetMethod('ReferenceEquals').Invoke($null, @($null, $param))
} $v2 # -> $true; would be the same with $v1 or no argument at all.
在[System.Management.Automation.Internal.AutomationNull]::Value
文档指出:
没有返回实际值的任何操作应返回AutomationNull.Value
。
一种评价Windows PowerShell中表达的任何组件时,应做好应对接收和丢弃这个结果。 当在需要的值的评估接收的,它应该被替换null
。
PSV2与PSv3 +和一般矛盾 :
PSV2提供没有区别[System.Management.Automation.Internal.AutomationNull]::Value
和$null
存储在变量值:
直接在使用无输出指令foreach
语句/管道没有按预期方式工作 -没有通过管道发送/中foreach
没有输入回路:
Get-ChildItem nosuchfiles* | ForEach-Object { 'hi' } foreach ($f in (Get-ChildItem nosuchfiles*)) { 'hi' }
相反, 如果无输出指令被保存在一个变量或显式$null
使用,该行为是不同的 :
# Store the output from a no-output command in a variable. $result = Get-ChildItem nosuchfiles* # PSv2-: quiet conversion to $null happens here # Enumerate the variable. $result | ForEach-Object { 'hi1' } foreach ($f in $result) { 'hi2' } # Enumerate a $null literal. $null | ForEach-Object { 'hi3' } foreach ($f in $null) { 'hi4' }
综上所述 ,PSv3 +行为 :
为了向后兼容的缘故,当前行为不能被改变。 GitHub上的这个评论提出了一种方法来解决这些矛盾的需要不能向后兼容未来潜在的PowerShell版本。
当您从一个PowerShell函数返回一个集合,在默认情况下PowerShell的决定返回值的数据类型,如下所示:
- 如果集合有一个以上的元素,返回的结果是一个数组。 需要注意的是返回结果的数据类型的System.Array即使正在返回的对象是不同类型的集合。
- 如果集合具有单个元件,所述返回的结果是,元素的值,而不是一个元件的集合,并返回结果的数据类型是该元素的数据类型。
- 如果集合为空,返回结果为$空
$l_t = @()
分配一个空数组到$ l_t。
$l_t2 = emptyArray
$空赋给$ l_t2,因为函数emptyArray返回一个空集,因此,返回结果为$空 。
$ l_t2和$ l_t3 都是空的,他们的行为方式相同。 既然你已经预先声明的$ l_t为空数组,当你添加或者$ l_t2或$ l_t3到它,无论是用+ =运营商或addToArray功能,一个元素被** $空*添加到其值阵列。
如果要强制函数,以便保持你返回集合对象的数据类型,用逗号:
PS> function emptyArray {,@()}
PS> $l_t2 = emptyArray
PS> $l_t2.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
PS> $l_t2.Count
0
注:emtpyArray后的空括号中的函数声明是多余的。 您只有在你使用它们来声明参数需要的函数名后括号内。
有趣的一点要注意的是,逗号操作
不一定能返回值的数组。
回想一下,我在第一点提到的,默认情况下有多个元素的集合返回结果的数据类型的System.Array无论收集的实际数据类型。 例如:
PS> $list = New-Object -TypeName System.Collections.Generic.List[int]
PS> $list.Add(1)
PS> $list.Add(2)
PS> $list.Count
2
PS> $list.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True List`1 System.Object
请注意,此集合的数据类型是List`1,不System.Array的 。
但是,如果从一个函数返回它,在函数内部$列表的数据类型是List`1,但它返回包含相同元素的System.Array。
PS> function Get-List {$list = New-Object -TypeName System.Collections.Generic.List[int]; $list.Add(1); $list.Add(2); return $list}
PS> $l = Get-List
PS> $l.Count
2
PS> $l.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
如果你想返回的结果是相同的数据类型,你返回函数中的一个集合,逗号运算符将实现这一目标:
PS> function Get-List {$list = New-Object -TypeName System.Collections.Generic.List[int]; $list.Add(1); $list.Add(2); return ,$list}
PS> $l = Get-List
PS> $l.Count
2
PS> $l.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True List`1 System.Object
这不仅限于类似数组的集合对象。 据我所看到的,任何时候PowerShell的改变你返回对象的数据类型,并希望返回值保存对象的原始数据类型,你可以做到这一点的物体前面所用逗号返回。 编写查询数据库,并返回一个DataTable对象的功能,当我第一次遇到这个问题。 返回结果是哈希表,而不是数据表的阵列。 更改return $my_datatable_object
到return ,$my_datatable_object
取得函数返回一个实际DataTable对象。