我最近在有一个对象数组的地方犯了一个错误,我不小心对数组的属性进行了布尔值评估,但是我 meant 对数组中的单个对象进行了评估。我仅在测试过程中发现错误,因为我期望$TRUE
时得到一个$FALSE
值。我创建了一个小脚本来说明这种情况。我不明白的是为什么这个不存在的数组属性总是求值为true?
# Set-StrictMode -Version Latest; # Catch dumb programmer mistakes.
$PSVersionTable.PSVersion
Write-Output "`n"
$coArray = @()
foreach ($id in 0,1)
{
$cObj = New-Object PSobject
$cObj | Add-Member -type NoteProperty -name ID -value $id
$cObj | Add-Member -type NoteProperty -name Bool -value $false
$coArray += $cObj
}
foreach ($cObj in $coArray)
{
# What I meant to do...
if ($cObj.Bool) { Write-Output 'ON OBJECT: Bool is true' }
else { Write-Output 'ON OBJECT: Bool is false' }
# What I actually did...
if ($coArray.Bool) { Write-Output 'ON ARRAY: Bool is true' }
else { Write-Output 'ON ARRAY: Bool is false' }
# What if there is no such object property?
if ($coArray.Bogus) { Write-Output 'ON ARRAY: Bogus is true' }
else { Write-Output 'ON ARRAY: Bogus is false' }
}
以下是输出:
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 2339
ON OBJECT: Bool is false
ON ARRAY: Bool is true
ON ARRAY: Bogus is true
ON OBJECT: Bool is false
ON ARRAY: Bool is true
ON ARRAY: Bogus is true
起初,我认为对于放错了位置的现有对象属性,它返回true,因为它确认了数组中的至少一个对象具有该属性。但是,如果您关闭strict并引用了一个完全伪造的属性(如上所示),它仍然会返回true。
答案 0 :(得分:2)
嗯...所以这就是我的想法。我认为这是Powershell现在在数组对象上展开属性的方式的影响。 Powershell绝对是真实的。
第一个测试if ($cObj.Bool)
确实按照人们期望的方式工作。第二个测试if ($coArray.Bool)
使powershell创建一个新数组,该数组仅包含每个对象中该属性的内容。可以通过以下方式显示:
C:\Users\Rob> $coArray.bool
False
False
因此在真实的Powershell世界中,这确实存在,因此是真的。
第三项测试if ($coArray.Bogus)
几乎具有相同的作用。可以通过以下方式显示:
Get-Member -InputObject $coArray.bogus
TypeName: System.Object[]
Name MemberType Definition
---- ---------- ----------
Count AliasProperty Count = Length
Add Method int IList.Add(System.Object value)
Address Method System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a...
Clear Method void IList.Clear()
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
CompareTo Method int IStructuralComparable.CompareTo(System.Object other, System.Collections.ICo...
Contains Method bool IList.Contains(System.Object value)
CopyTo Method void CopyTo(array array, int index), void CopyTo(array array, long index), void...
Equals Method bool Equals(System.Object obj), bool IStructuralEquatable.Equals(System.Object ...
Get Method System.Object Get(int )
GetEnumerator Method System.Collections.IEnumerator GetEnumerator(), System.Collections.IEnumerator ...
GetHashCode Method int GetHashCode(), int IStructuralEquatable.GetHashCode(System.Collections.IEqu...
GetLength Method int GetLength(int dimension)
GetLongLength Method long GetLongLength(int dimension)
GetLowerBound Method int GetLowerBound(int dimension)
GetType Method type GetType()
GetUpperBound Method int GetUpperBound(int dimension)
GetValue Method System.Object GetValue(Params int[] indices), System.Object GetValue(int index)...
IndexOf Method int IList.IndexOf(System.Object value)
Initialize Method void Initialize()
Insert Method void IList.Insert(int index, System.Object value)
Remove Method void IList.Remove(System.Object value)
RemoveAt Method void IList.RemoveAt(int index)
Set Method void Set(int , System.Object )
SetValue Method void SetValue(System.Object value, int index), void SetValue(System.Object valu...
ToString Method string ToString()
Item ParameterizedProperty System.Object IList.Item(int index) {get;set;}
IsFixedSize Property bool IsFixedSize {get;}
IsReadOnly Property bool IsReadOnly {get;}
IsSynchronized Property bool IsSynchronized {get;}
Length Property int Length {get;}
LongLength Property long LongLength {get;}
Rank Property int Rank {get;}
SyncRoot Property System.Object SyncRoot {get;}
因此,正如所指出的,仅在数组中执行一个对象会导致所有测试为假。这是因为数组中只有一项意味着它只有一个属性可以展开。 Powershell将其展开为单例对象而不是值数组。因此,如果没有名称相同的属性,则单例为null。看到这里:
C:\Users\Rob> $b = @((New-Object PSCustomObject -Property @{'notBogus'='foo'}))
C:\Users\Rob> $b
notBogus
--------
foo
C:\Users\Rob> gm -in $b.Notbogus
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
CompareTo Method int CompareTo(System.Object value), int CompareTo(string strB), int IComparab...
Contains Method bool Contains(string value)
CopyTo Method void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int co...
EndsWith Method bool EndsWith(string value), bool EndsWith(string value, System.StringCompari...
.
.
.
C:\Users\Rob> $b.bogus -eq $null
True
如您所见,$ b.NotBogus只是一个值,因此可以进行适当测试。
仍然不确定@tessellatingHeckler提出的反例。我会继续寻找。
答案 1 :(得分:1)
让我们从以下基本行为入手:
PSv3引入了member enumeration,该功能可以直接访问集合(数组)上的属性并具有 elements'属性的数组值返回。
([pscustomobject] @{foo=1}, [pscustomobject] @{foo=2}).foo
返回由.foo
个值组成的数组,即1, 2
。除非有效Set-StrictMode -Version 2
或更高,否则访问对象上的不存在的属性会返回$null
。
$null -eq ($PSVersionTable).Bogus
产生$True
具有 2个或更多元素的数组是总是真实的(在布尔上下文中的值为$True
),而与元素值无关,例如,即使它们都是$null
。
[bool] ($null, $null)
产生$True
因此,假设您的$coArray
数组具有2个元素,即使访问伪属性也是真实的,因为if ($coArray.Bogus)
实际上与if ($null, $null)
相同,并且如上所述,($null, $null)
是真:
if ($null, $null) { 'true' } # -> 'true', because the conditional is a 2+-element array
但是,奇怪的是,为伪造的属性返回$null
的数组,似乎仅适用于自定义对象 ({[pscustomobject]
个实例):
# Accessing a nonexistent property on a collection of *custom objects*
# returns a $null for each element:
PS> ([pscustomobject] @{foo=1}, [pscustomobject] @{foo=2}).Bogus.GetType().Name
Object[] # 2-element array whose elements are $null
# By contrast, *other types* seem to evaluate to a $null *scalar* (just $null),
# as evidenced by the .GetType() call failing:
PS> ($PSVersionTable, $PSVersionTable).Bogus.GetType().Name
You cannot call a method on a null-valued expression.
在this GitHub issue中讨论了这种不一致之处。