为什么 [NullString]::Value 使用断点的评估方式不同?

Why does [NullString]::Value evaluate differently with a breakpoint?

我已经在 PowerShell ISE 和 VS Code 中尝试过这段代码,结果同样奇怪。如果没有断点,输出为 EMPTY,但在带有 "NULL" 的行中有一个断点,输出为 NULL(如预期)。为什么?

function demo {
    param(
        [string] $value = [NullString]::Value
    )

    if ($null -eq $value) {
        "NULL"
    } elseif ($value -eq '') {
        "EMPTY"
    } else {
        "$value"
    }
}

demo

我现在知道,当您对参数使用类型修饰符 [string] 时,PowerShell 始终会将非字符串值(例如 $null 或 [NullString]::Value)转换为(空)字符串. 好吧,我可以接受,但如果调试在这种情况下很奇怪,你自己很难弄明白。

Difference between $null and ""

Empty string is not the same as null; you'd need to test specifically for that. Null is the non-existence of an object, Whereas a string is an object even if it's empty.

That's a common gotcha in PowerShell. For whatever reason, it won't let you assign a $null value to a string variable (or a string parameter to a .NET type); it gets converted to an empty string. This can cause some headaches when calling .NET methods that treat null and empty strings differently, which is why they later added (in v3, if I remember correctly), the [System.Management.Automation.NullString] class. If you want to call such a method, you do this:

[SomeClass]::SomeMethod([nullstring]::Value)

[string]$camp = $null

Will assign $Null to $Camp, but since the [string]-part forces $camp to be of the type string, $Camp will be assigned the value of [String]$Null

[String]$Null will force a conversion of $Null (which is basically a non-existing object) to a string and in PowerShell that results in an empty string.

As far as PowerShell is concerned, that's basically correct. However, in the .NET Framework, strings really can be null, and there's a difference between a null string reference and an empty string. Sometimes that causes confusion when you're looking at .NET documentation and wondering why it doesn't seem to work properly from PowerShell.

Use [string]::IsNullOrEmpty($variable)

https://powershell.org/forums/topic/difference-between-null-and/

另见...

How can I check if a string is null or empty in PowerShell? [string]::IsNullOrEmpty(...)

How can I check if a string is null or empty in PowerShell?

* 更新 *

正在将您的代码更改为...

function demo {
    param(
        [string]$value
    )

    if ([string]::IsNullOrEmpty($value))
    {
        "NULL"
    } elseif ($value -eq '') {
        "EMPTY"
    } else {
        "$value"
    }
}

有或没有调试(在 'NULL' 和 'EMPTY' 上设置断点)努力,返回的结果在我的系统上是相同的 ISE/VSCode

# Results 

demo
NULL

demo -value ''
NULL

demo -value ' '


demo -value test
test

* 修改以显示 elseif 处理空格 *

具有或具有先前所述的条件

 function demo {
    param(
        [string]$value
    )

    if ([string]::IsNullOrEmpty($value))
    {
        "NULL"
    } elseif ([string]::IsNullOrWhiteSpace($value)) {
        "EMPTY or a White Space"
    } else {
        "$value"
    }
}

# Results

demo
NULL
demo -value ''
NULL
demo -value ' '
EMPTY or a White Space
demo -value test
test

PetSerAl,一如既往,在对问题的评论中提供了关键指针:

A known optimization bug 是最可能的原因(从 Windows PowerShell v5.1 / PowerShell Core v6.1.0 开始),那个错误只是masked 当 Visual Studio 代码或 ISE debugger.

中的代码为 运行 时

因此,您可以使用链接错误报告中提到的相同 解决方法:在函数体的任何位置调用 Remove-Variable(它的 存在 就足够了 - 实际上不需要在 运行 时间进行调用):

function demo {
    param(
        [string] $value = [NullString]::Value
    )

    # Workaround for optimization bug
    if ($False) { Remove-Variable }

    if ($null -eq $value) {
        "NULL"
    } elseif ($value -eq '') {
        "EMPTY"
    } else {
        "$value"
    }
}

demo

现在无论调试与否,您始终得到 "NULL" 作为输出。

但是,最好将 [NullString]::Value 的使用限制在其设计目的范围内:将 null 传递给 [=67= 的 string 类型参数.NET 方法 - 见下文。


至于 为什么需要使用 [NullString]::Value 才能将 $null 传递给字符串参数/将 $null 存储在 [string] 变量,鉴于.NET字符串通常可以直接存储null$null):

根据(历史)设计,当您将 $null 分配给 [string] 变量时,PowerShell 会将 $null 转换为 ''(空字符串);理由如下:

来自 https://github.com/PowerShell/PowerShell/issues/4616#issuecomment-323530442:

The thinking behind the design was that in most ways, $null and the empty string both represent the same error condition and that in the rare case where a distinction was important, $PSBoundParameters would be sufficient to distinguish between knowing a value was provided or not.

鉴于 even passing $null directly performs conversion to '' when passing arguments to string-typed .NET methods,您不能将 null 传递给 v2 之前的此类方法。
为了解决这个问题,版本 3 引入了 [NullString]::Value,它明确表示希望在字符串上下文中传递 $null
(替代方案 - 使 PowerShell 字符串默认为 $null 并允许直接分配 $null - 被认为是会破坏太多现有脚本的更改。)

使用 [NullString]::Value 超出其预期目的 - 即在 .NET 方法中将 null 传递给 string 参数 - 是有问题的,因为 PowerShell 不不期望 [string] 变量在其他上下文中包含 $null

修复上述优化bug对题中场景有帮助,但可能还有其他陷阱。