在 PowerShell 中,为什么 $null -lt 0 = $true?那可靠吗? [英] In PowerShell, why is $null -lt 0 = $true? Is that reliable?
问题描述
考虑以下 PowerShell 代码:
Consider the following PowerShell code:
> $null -gt 0
False
> $null -ge 0
False
> $null -eq 0
False
> $null -le 0
True
> $null -lt 0
True
当然,对于显式设置为 $null
的 $variable 或不存在的变量也是如此.
Of course the same is true for a $variable explicitly set to $null
or for a non-existent variable.
- 这是为什么?这对我来说没有多大意义.我觉得 $null 根据定义没有可以这样测试的值,或者至少,它在这样的测试中评估为零.但除此之外,我想我不知道我实际上会期望什么行为.谷歌搜索(或搜索 SO)例如为什么在 Powershell 中 null 小于零"似乎没有产生任何结果,但我确实看到了其他几种语言的相关问题和答案.
- 可以并且应该依赖这个结果吗?
- 除了使用 GetType() 或IsNumeric"、IsNullOrEmpty"等的各种实现测试变量之外,什么是可靠测试的最佳(即最简洁、最佳性能等)方法可能具有
$null
值的变量中的整数值(或其他类型)?或者其中一种方法被认为非常标准?
- Why is that? It doesn't make a lot of sense to me. I feel like $null by definition doesn't have a value that could be tested as such, or at the very least, that it would evaluate to zero in such tests. But other than that I guess I don't know what behavior I would actually expect. Googling (or searching SO) for e.g. "Why is null less than zero in Powershell" doesn't seem to yield any results, though I do see relevant questions and answers for several other languages.
- Can and should this result be relied on?
- Aside from testing the variable with GetType(), or various implementations of "IsNumeric", "IsNullOrEmpty", etc. what is a the best (i.e. most concise, best performing, etc.) way to reliably test for integer values (or other types for that matter) in a variable that might have a value of
$null
? Or is one of those methods considered pretty standard?
感谢您的时间.如果这太软弱",请提前道歉;关于这个场地的问题.
Thanks for your time. Apologies in advance if this is too "squishy" of a question for this venue.
附言我常用的环境是 PowerShell v5.1.
P.S. For what it's worth my usual environment is PowerShell v5.1.
推荐答案
这是为什么?
行为违反直觉:
运算符 -lt
、-le
、-gt
、-ge
,即使它们可以 also 具有 numeric 含义,似乎将 $null
操作数视为空字符串(''
),即它们默认为字符串比较,如postanote 的有用回答 暗示.
Operators -lt
, -le
, -gt
, -ge
, even though they can also have numeric meaning, seemingly treat a $null
operand as if it were the empty string (''
), i.e. they default to string comparison, as the sample commands in postanote's helpful answer imply.
也就是说,$null -lt 0
实际上与 '' -lt '0'
的计算结果相同,这解释了$true
结果,因为在词法比较中条件满足.
虽然您也可以将 $null -eq 0
设想为 '' -eq '0'
,-eq
情况是特殊的 - 见下文.
That is, $null -lt 0
is in effect evaluated the same as '' -lt '0'
, which explains the $true
result, because in lexical comparison the condition is met.
While you can conceive of $null -eq 0
as '' -eq '0'
too, the -eq
case is special - see below.
此外,将 0
放在 LHS 上仍然像字符串比较一样(除了 -eq
见下文) -即使通常是 LHS 的类型导致 RHS 被强制为相同类型.
Additionally, placing the 0
on the LHS still acts like a string comparison (except with -eq
see below) - even though it is normally the type of the LHS that causes the RHS to be coerced to the same type.
也就是说,0 -le $null
似乎也像 '0' -le ''
,因此返回 $false
.
That is, 0 -le $null
too seems to act like '0' -le ''
and therefore returns $false
.
虽然这种行为在排他性基于字符串的运算符中是可以预料的,例如 -match
和 -like
,对于还支持数字的运算符来说令人惊讶,特别是考虑到其他此类运算符 - 以及那些独占数字的运算符 - 默认为数字对$null
的解释,如0
.
While such behavior is to be expected in operators that are exclusively string-based, such as -match
and -like
, it is surprising for operators that also support numbers, especially given that other such operators - as well as those that are exclusively numeric - default to numeric interpretation of $null
, as 0
.
+
、-
和/
do 强制 LHS$null
到0
(默认为[int]
);例如$null + 0
是0
*
没有;例如,$null * 0
又是$null
.
+
,-
, and/
do force a LHS$null
to0
([int]
by default); e.g.$null + 0
is0
*
does not; e.g.,$null * 0
is again$null
.
其中-
和/
只是数字,而+
和*
也适用于 string 和 array 上下文.
Of these, -
and /
are exclusively numeric, whereas +
and *
also work in string and array contexts.
还有一个额外的不一致:-eq
从不对 $null
操作数执行类型强制:
There is an additional inconsistency: -eq
never performs type coercion on a $null
operand:
$null -eq <RHS>
永远是$true
如果
也是$null
(或自动化 null" - 见下文),并且是目前可靠地测试值是否为$null
的唯一方法.(换种说法:$null -eq ''
not 与'' -eq ''
- 没有类型强制在这里发生.)
$null -eq <RHS>
is only ever$true
if<RHS>
is also$null
(or "automation null" - see below), and is currently the only way to reliably test a value for being$null
. (To put it differently:$null -eq ''
is not the same as'' -eq ''
- no type coercion takes place here.)
- GitHub PR #10704 不幸停滞不前,旨在实施一个专门的
$null
测试的语法,例如
.-is $null
- GitHub PR #10704, which has unfortunately stalled, aims to implement a dedicated syntax for
$null
tests, such as<LHS> -is $null
.
同样,
也不会对 $null
执行类型强制,并且只返回 $true
和 $null
作为LHS;
Similarly, <LHS> -eq $null
also performs no type coercion on $null
and returns $true
only with $null
as the LHS;
- 然而,对于数组值的
,-eq
充当过滤器(与大多数运算符一样),返回$null
元素的 子数组;例如,1, $null, 2, $null, 3 -eq $null
返回 2 元素数组$null, $null
. - 这种过滤行为的原因是只有
$null -eq <RHS>
- 使用$null
作为标量 LHS - 作为 (scalar)$null
.
- However, with an array-valued
<LHS>
,-eq
acts as filter (as most operators do), returning the subarray of elements that are$null
; e.g.,1, $null, 2, $null, 3 -eq $null
returs 2-element array$null, $null
. - This filtering behavior is the reason that only
$null -eq <RHS>
- with$null
as the scalar LHS - is reliable as a test for (scalar)$null
.
请注意,行为同样适用于自动化空值";PowerShell 用来表达命令的(非)输出的值(从技术上讲,[System.Management.Automation.Internal.AutomationNull]::Value
单例),因为这个值与表达式中的$null
相同;例如$(& {}) -lt 0
也是 $true
- 参见 这个答案了解更多信息.
Note that the behaviors equally apply to the "automation null" value that PowerShell uses to express the (non-)output from commands (technically, the [System.Management.Automation.Internal.AutomationNull]::Value
singleton), because this value is treated the same as $null
in expressions; e.g. $(& {}) -lt 0
is also $true
- see this answer for more information.
同样,这些行为也适用于可空值类型的实例,这些实例碰巧包含$null
(例如,[System.Nullable[int]] $x = $null; $x -lt 0
也是 $true
)谢谢,Dávid Laczkó.,但请注意,它们在 PowerShell 中的使用很少.
Similarly, the behaviors also apply to instances of nullable value types that happen to contain $null
(e.g., [System.Nullable[int]] $x = $null; $x -lt 0
is also $true
)Thanks, Dávid Laczkó., though note that their use in PowerShell is rare.
可以并且应该依赖这个结果吗?
Can and should this result be relied on?
由于操作员之间的行为不一致,我不会依赖它,尤其是因为也很难记住哪些规则适用于何时 - 并且至少存在不一致的假设可能性固定;鉴于这相当于一个破坏性变化,但是,这可能不会发生.
Since the behavior is inconsistent across operators, I wouldn't rely on it, not least because it's also hard to remember which rules apply when - and there's at least a hypothetical chance that the inconsistency will be fixed; given that this would amount to a breaking change, however, that may not happen.
如果向后兼容性不是问题,以下行为将消除不一致并制定易于概念化和记忆的规则:
If backward compatibility weren't a concern, the following behavior would remove the inconsistencies and make for rules that are easy to conceptualize and remember:
当(基本标量)二元运算符被赋予一个 $null
操作数以及一个非 $null
操作数时 - 无论哪个是 LHS,哪个是RHS:
When a (fundamentally scalar) binary operator is given a $null
operand as well as a non-$null
operand - irrespective of which is the LHS and which is the RHS:
对于在数字/布尔/字符串操作数上独占操作的运算符(例如
/
/-and
/-match
):将$null
操作数强制为运算符隐含的类型.
For operators that operate exclusively on numeric / Boolean / string operands (e.g.
/
/-and
/-match
): coerce the$null
operand to the type implied by the operator.
对于在多个域"中操作的运算符;- 文本和数字(例如 -eq
) - 将 $null
操作数强制转换为 other 操作数的类型.
For operators that operate in multiple "domains" - both textual and numeric (e.g. -eq
) - coerce the $null
operand to the other operand's type.
请注意,这将额外需要使用不同语法的专用 $null
测试,例如上述 PR 中的 -is $null
.
Note that this would then additionally require a dedicated $null
test with different syntax, such as the -is $null
from the above-mentioned PR.
注意:上述内容不适用于 collection 运算符,-in
和 -contains
(和它们的否定变体 -notin
和 -notcontains
),因为它们的元素相等比较的作用类似于 -eq
,因此从不将类型强制应用于 <代码>$null 值.
Note: The above does not apply to the collection operators, -in
and -contains
(and their negated variants -notin
and -notcontains
), because their element-wise equality comparison acts like -eq
and therefore never applies type coercion to $null
values.
在可能具有 $ 值的变量中可靠地测试整数值(或其他类型)的最佳(即最简洁、最佳性能等)方法是什么?空?
what is the best (i.e. most concise, best performing, etc.) way to reliably test for integer values (or other types for that matter) in a variable that might have a value of $null?
以下解决方案将 $null
操作数强制为 0
:
The following solutions force a $null
operand to 0
:
- 注意:下面
-lt
操作的 LHS 周围的(...)
用于概念清晰,但不是绝对必要的 - 请参阅 about_Operator_Precedence.
- Note:
(...)
around the LHS of the-lt
operations below is used for conceptual clarity, but isn't strictly necessary - see about_Operator_Precedence.
在 PowerShell (Core) 7+ 中,使用 ??
、null-coalescing operator,它与任何类型:
In PowerShell (Core) 7+, use ??
, the null-coalescing operator, which works with operands of any type:
# PowerShell 7+ only
($null ?? 0) -lt 0 # -> $false
在不支持此运算符的 Windows PowerShell 中,使用虚拟计算:
In Windows PowerShell, where this operator isn't supported, use a dummy calculation:
# Windows PowerShell
(0 + $null) -lt 0 # -> $false
虽然像 [int] $null -lt 0
之类的东西也能工作,但它要求你提交一个 特定 数字类型,所以如果操作数碰巧更高比[int]::MaxValue
,表达式会失败;[double] $null -lt 0
可以将这种风险降到最低,但至少在假设上会导致准确性的损失.
While something like [int] $null -lt 0
works too, it requires you to commit to a specific numeric type, so if the operand happens to be higher than [int]::MaxValue
, the expression will fail; [double] $null -lt 0
would minimize that risk, though could at least hypothetically result in loss of accuracy.
虚拟添加 (0 +
) 绕过了这个问题,并且让 PowerShell 应用其通常的按需类型扩展.
The dummy addition (0 +
) bypasses this problem and lets PowerShell apply its usual on-demand type-widening.
顺便说一句:这种自动类型扩展也可能表现出意想不到的行为,因为其结果需要比任一操作数的类型都能容纳的更宽类型的全整数计算总是被扩展为 [double]
,即使更大的整数类型就足够;例如([int]::MaxValue + 1).GetType().Name
返回 Double
,即使 [long]
结果就足够了,导致潜在的准确性损失 - 有关详细信息,请参阅此答案.
As an aside: This automatic type-widening can exhibit unexpected behavior too, because an all-integer calculation whose result requires a wider type than either operand's type can fit is always widened to [double]
, even when a larger integer type would suffice; e.g. ([int]::MaxValue + 1).GetType().Name
returns Double
, even though a [long]
result would have sufficed, resulting in potential loss of accuracy - see this answer for more information.
这篇关于在 PowerShell 中,为什么 $null -lt 0 = $true?那可靠吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!