在 PowerShell 中,为什么 $null -lt 0 = $true?那可靠吗?

mms*_*eng 6 powershell null

考虑以下 PowerShell 代码:

> $null -gt 0
False
> $null -ge 0
False
> $null -eq 0
False
> $null -le 0
True
> $null -lt 0
True
Run Code Online (Sandbox Code Playgroud)

当然,对于显式设置为$null或不存在变量的 $variable 也是如此。

  1. 这是为什么?这对我来说没有多大意义。我觉得 $null 根据定义没有可以这样测试的值,或者至少,它在这样的测试中评估为零。但除此之外,我想我不知道我实际上会期望什么行为。谷歌搜索(或搜索 SO)例如“为什么在 Powershell 中 null 小于零”似乎没有产生任何结果,尽管我确实看到了其他几种语言的相关问题和答案。
  2. 可以而且应该依赖这个结果吗?
  3. 除了使用 GetType() 或“IsNumeric”、“IsNullOrEmpty”等的各种实现测试变量之外,可靠地测试整数值(或其他类型)在一个可能具有值的变量中$null?或者这些方法之一被认为是非常标准的?

谢谢你的时间。如果这个问题对这个场地来说太“模糊”,请提前道歉。

PS我通常的环境是PowerShell v5.1。

mkl*_*nt0 7

\n

这是为什么?

\n
\n

这种行为是违反直觉的

\n

运算符-lt, -le, -gt, -ge,尽管它们可以具有数字含义,但似乎将$null操作数视为空字符串\'\'),即它们默认为string Comparison ,正如postanote\'s 有用答案中的示例命令所暗示的那样。

\n

也就是说,$null -lt 0实际上 的计算结果与 相同\'\' -lt \'0\',这解释了$true结果,因为在词法比较中满足了条件。\n虽然您也可以这样
想,但情况很特殊- 见下文。$null -eq 0\'\' -eq \'0\'-eq

\n

此外,将 放在LHS0上仍然起到字符串比较的作用(除非见下文) - 尽管通常是 LHS 的类型导致 RHS 被强制为相同的类型-eq

\n

也就是说,0 -le $null太似乎表现得像\'0\' -le \'\',因此返回$false

\n

虽然这种行为在完全基于字符串的运算符(例如-matchand )中是预料之中的,但对于也支持数字-like的运算符来说,这是令人惊讶的,特别是考虑到其他此类运算符 - 以及那些完全基于数字的运算符 - 默认为数字解释的$null, 作为0.

\n
    \n
  • +-/ do强制 LHS (默认情况$null下);例如是0[int]$null + 00
  • \n
  • *不是; 例如,$null * 0又是$null
  • \n
\n

其中,-完全/是数字,而和也可以在字符串数组上下文中使用。+*

\n

还有一个额外的不一致:-eq 从不$null对操作数执行类型强制

\n
    \n
  • $null -eq <RHS>仅当$trueis <RHS>is$null(或“自动化 null” - 见下文)时才为 ,并且是目前可靠地测试 being 值的唯一方法$null。(换句话来说:与-此处发生类型强制$null -eq \'\'不同。)\'\' -eq \'\'

    \n\n
  • \n
  • 同样,<LHS> -eq $null也不执行任何类型强制,$null并且$true仅返回$nullLHS;

    \n
      \n
    • 但是,对于数组-valued <LHS>-eq充当过滤器(就像大多数运算符所做的那样),返回元素的子数组$null;例如,1, $null, 2, $null, 3 -eq $null返回 2 元素数组$null, $null
    • \n
    • $null -eq <RHS>这种过滤行为是只有- 作为$null标量 LHS - 作为 (标量) 的测试是可靠的原因$null
    • \n
    \n
  • \n
\n

请注意,这些行为同样适用于PowerShell 用于表达命令(技术上讲,[System.Management.Automation.Internal.AutomationNull]::Value单例)的(非)输出的“自动化 null”值,因为该值的处理方式与表达式$null中的相同;例如也是- 请参阅此答案以获取更多信息。$(& {}) -lt 0$true

\n

同样,这些行为也适用于恰好包含(例如,也是)的可空值类型的实例,谢谢,D\xc3\xa1vid Laczk\xc3\xb3,但请注意,它们在 PowerShell 中的使用很少。$null[System.Nullable[int]] $x = $null; $x -lt 0$true

\n
\n
\n

这个结果可以而且应该被依赖吗?

\n
\n

由于操作员之间的行为不一致,因此我不会依赖它,尤其是因为也很难记住何时应用哪些规则 - 并且至少有一个假设的机会可以修复不一致的情况;然而,考虑到这将构成重大改变,这种情况可能不会发生。

\n

如果不考虑向后兼容性,则以下行为将消除不一致并制定易于概念化和记住的规则

\n

当(本质上是标量)二元运算符被赋予一个$null操作数和一个非$null操作数时 - 无论哪一个是左侧,哪个是右侧:

\n
    \n
  • 对于专门对数字/布尔/字符串操作数进行操作的运算符(例如// /):将操作数强制为运算符隐含的类型。-and-match$null

    \n
  • \n
  • 对于在多个“域”中操作的运算符- 文本和数字(例如-eq) - 将$null操作数强制为另一个操作数的类型。

    \n
  • \n
\n

请注意,这还需要$null使用不同语法进行专门测试,例如-is $null上述 PR 中的 。

\n

注意:上面的内容不适用于集合运算-inand -contains(以及它们的否定变体-notinand -notcontains),因为它们的元素相等比较的行为类似于-eq,因此永远不会对$null值应用类型强制。

\n
\n
\n

可靠地测试可能值为 $null 的变量中的整数值(或其他类型)的最佳(即最简洁、性能最佳等)方法是什么?

\n
\n

以下解决方案强制$null操作数0

\n
    \n
  • 注意:下面(...)操作的 LHS-lt是为了概念清晰而使用的,但这并不是绝对必要的 - 请参阅about_Operator_Precedence
  • \n
\n

PowerShell (Core) 7+中,使用空合并运算符??,它适用于任何类型的操作数:

\n
# PowerShell 7+ only\n($null ?? 0) -lt 0 # -> $false\n
Run Code Online (Sandbox Code Playgroud)\n

在不支持此运算符的Windows PowerShell中,请使用虚拟计算

\n
# Windows PowerShell\n(0 + $null) -lt 0  # -> $false\n
Run Code Online (Sandbox Code Playgroud)\n

虽然类似的东西[int] $null -lt 0也可以工作,但它要求您提交特定的数字类型,因此如果操作数碰巧高于[int]::MaxValue,则表达式将失败;[double] $null -lt 0可以最大限度地降低这种风险,尽管至少在假设上可能会导致准确性的损失。

\n

虚拟添加( 0 +) 绕过了这个问题,让 PowerShell 应用其通常的按需类型加宽

\n

顺便说一句:这种自动类型加宽也可能表现出意想不到的行为,因为全整数计算的结果需要比任一操作数的类型可以容纳的类型更宽的类型[double],即使更大的整数类型就足够了,它也总是被加宽到;例如([int]::MaxValue + 1).GetType().Namereturn Double,即使[long]结果就足够了,导致潜在的准确性损失 - 请参阅此答案以获取更多信息。

\n