测试PSCustomObject的属性时,为什么操作数的顺序很重要 [英] Why does the order of operands matter when testing for property of PSCustomObject

查看:69
本文介绍了测试PSCustomObject的属性时,为什么操作数的顺序很重要的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在if语句中尝试了两种情况$psCustomObject.x -eq $null$null -eq $psCustomObject.x,只有后者通过了if. 为什么这样表现呢?似乎不合逻辑.

我的特定用例是一个json文件,其中包含多个环境的配置.环境的名称是对象中的关键,我的目标是在目标不存在的环境(可能是拼写错误)时通知用户,然后停止.

我不想使用! opreator,因为它具有极高的信息与代码比率-很容易忽略,但是却有很大的不同,因此,我通常不喜欢它.它还需要了解该语言的工作原理(powershell不是静态类型的,我不希望读者需要了解什么是真实的还是不真实的).这听起来可能很愚蠢,但是powershell不是我的主要语言,也不是我的同事的语言.可读性是我的关键因素.

解决方案

tl; dr

要使用-eq-ne将值与$null进行比较,始终将$null设为LHS :

$null -eq $psCustomObject.x  # NOT $psCustomObject.x -eq $null

  • 操作数的顺序很重要,因为PowerShell中的比较运算符用作具有 array-valued LHS值的 filters .

  • 此外,即使与$null以外的其他内容进行比较,由于隐式类型转换,操作数的顺序也可能很重要.


PowerShell的 比较运算符(例如-eq )在设计上与数组值 LHS 的行为不同,因此在重要位置放置什么操作数,甚至对于通常可交换的运算符
-eq-ne .

  • 使用标量 LHS (单个值),比较运算符返回布尔值 ($True$False表示比较结果.

    • 但是,即使具有标量操作数放置也很重要,如 Jeff Zeitlin 所述,即使用不同类型的操作数:通常,在比较之前,RHS操作数被强制转换为LHS的数据类型;例如,' 2 ' -eq 2执行 string 比较(将整数2强制转换为字符串'2'),因此返回$False,而2 -eq ' 2 '执行 integer 比较(转换字符串' 2 '[int]),因此返回$True.
  • 使用 数组值的LHS (是集合的LHS值),比较运算符返回数组 ([object[]]),因为它充当过滤器 :运算符分别应用于输入数组的元素 ,返回的是操作为其返回$True的那些元素的子数组.

请注意,从Windows PowerShell v5.1/PowerShell Core 6.1.0开始,PowerShell仅将数组值操作数支持为 LHS 操作数; RHS操作数必须为标量或被强制为1. [1]


因此,在您的示例中, $null -eq $psCustomObject.x$psCustomObject.x -eq $null不能互换,并测试不同的条件:

# Is the RHS $null?
# Whether the concrete RHS value is then a scalar or an array doesn't matter.
$null -eq $psCustomObject.x

# * If $psCustomObject.x is a scalar: is that scalar $null?
# * If $psCustomObject.x is an ARRAY: 
#   RETURN THE SUB-ARRAY OF ELEMENTS THAT ARE $null
$psCustomObject.x -eq $null


在布尔上下文中使用(例如在if语句中)时,将评估数组(例如使用数组值的LHS返回的数组),如下: PetSerAl 求助. >

  • 一个数组或一个包含空数组的 1-element数组的值为$False.
  • 包含标量的 1元素数组计算得出该标量的(隐含)布尔值,例如[bool] @(0)实际上与[bool] 0相同,即$False.
  • 2+元素数组或包含非空数组 1-element数组始终为 $True其元素的值(例如,
    [bool] ($False, $False)[bool] (, (, $False))均为$True.

注意:术语 array 在上面使用得比较宽松.严格来说,以上内容适用于实现 [System.Collections.IList] 界面-请参见 解决方案

tl;dr

To compare a value to $null with -eq or -ne, always make $null the LHS:

$null -eq $psCustomObject.x  # NOT $psCustomObject.x -eq $null

  • The order of operands matters, because comparison operators in PowerShell act as filters with array-valued LHS values.

  • Additionally, even when comparing to something other than $null, the order of operands may matter due to implicit type conversions.


PowerShell's comparison operators, such as -eq, by design act differently with an array-valued LHS and therefore what operand is placed where matters even for the normally commutative operators
-eq and -ne.

  • With a scalar LHS (a single value), a comparison operator returns a Boolean ($True or $False that indicates the outcome of the comparison.

    • However, even with scalars operand placement can matter, as Jeff Zeitlin notes, namely with operands of different types: typically, the RHS operand is coerced to the data type of the LHS before comparing; e.g., ' 2 ' -eq 2 performs string comparison (coerces integer 2 to string '2') and therefore returns $False, whereas 2 -eq ' 2 ' performs integer comparison (converts string ' 2 ' to an [int]) and therefore returns $True.
  • With an array-valued LHS (a LHS values that is a collection), a comparison operator returns an array ([object[]]), because it acts as a filter: the operator is applied to the input array's elements individually, and what is returned is the sub-array of those elements for which the operation returned $True.

Note that, as of Windows PowerShell v5.1 / PowerShell Core 6.1.0, PowerShell supports array-valued operands only as the LHS operand; RHS operands must be scalars or are coerced to one.[1]


Therefore, in your example, $null -eq $psCustomObject.x and $psCustomObject.x -eq $null are not interchangeable and test different conditions:

# Is the RHS $null?
# Whether the concrete RHS value is then a scalar or an array doesn't matter.
$null -eq $psCustomObject.x

# * If $psCustomObject.x is a scalar: is that scalar $null?
# * If $psCustomObject.x is an ARRAY: 
#   RETURN THE SUB-ARRAY OF ELEMENTS THAT ARE $null
$psCustomObject.x -eq $null


When used in a Boolean context such as in an if statement, arrays, such as those returned with an array-valued LHS, are evaluated as follows:Tip of the hat to PetSerAl for his help.

  • An empty array or a 1-element array containing an empty array evaluates to $False.
  • A 1-element array containing a scalar evaluates to the (implied) Boolean value of that scalar, e.g., [bool] @(0) is effectively the same as [bool] 0, i.e., $False.
  • A 2+-element array or a 1-element array that contains a non-empty array is always $True, irrespective of its elements' values (e.g.,
    both [bool] ($False, $False) and [bool] (, (, $False)) are $True.

Note: The term array is used loosely above. Strictly speaking, the above applies to instances of any type that implements the [System.Collections.IList] interface - see the source code.
In addition to arrays, this includes types such as [System.Collections.ArrayList] and [System.Collections.Generic.List[<type>]] .


Example of when the two comparisons evaluate differently in a Boolean context:

Note:

  • This example is somewhat contrived - do tell us if there are better ones.
    Easier examples can be provided with operator -ne.

  • I couldn't come up with an example for the specific behavior you describe, where $null -eq $psCustomObject.x returns $True, but $psCustomObject.x -eq $null doesn't. If that is indeed what you saw, please tell us the specific .x value involved.

# Construct a custom object with an array-valued .x property that
# contains at least 2 $null values.
$psCustomObject = [pscustomobject] @{ x = @(1, $null, 2, $null) }

# $False, as expected: the value of .x is an array, and therefore not $null
[bool] ($null -eq $psCustomObject.x) 

# !! $True, because because 2 elements in the input array (.x)
# !! are $null, so a 2-element array - ($null, $null) - is returned, which in a
# !! Boolean context is always $True.
[bool] ($psCustomObject.x -eq $null) 


[1] In the docs, -replace is grouped in with the comparison operators, and technically its RHS is an array, but the elements of this array are the inherently scalar operands: the regex to match, and the replacement string.

这篇关于测试PSCustomObject的属性时,为什么操作数的顺序很重要的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆