在 PowerShell 中隐式转换为 C# 中定义的结构的 bool 失败

Implicit conversion to bool of struct defined in C# fails in PowerShell

为什么隐式转换为 [byte] 有效,但当用 bool 替换 byte 时它不再有效?

我。 e.以下作品...

Add-Type -TypeDefinition @'
public readonly struct MyByte
{
    private readonly byte value;

    public MyByte( byte b ) => this.value = b;

    public static implicit operator byte( MyByte b ) => b.value;
    public static explicit operator MyByte( byte b ) => new MyByte( b );

    public override string ToString() => $"{value}";
}
'@

[byte] $d = [MyByte]::new( 1 )    # OK

...虽然这个非常相似的代码没有:

Add-Type -TypeDefinition @'
public readonly struct MyBool
{
    private readonly bool value;

    public MyBool( bool b ) => this.value = b;

    public static implicit operator bool( MyBool b ) => b.value;
    public static explicit operator MyBool( bool b ) => new MyBool( b );

    public override string ToString() => $"{value}";
}
'@

[bool] $b = [MyBool]::new( $true )    # Error

这会产生以下错误:

Cannot convert value "MyBool" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or 0.

请注意,在 C# 中,到 bool 的隐式转换按预期工作:

public class MyBoolTest {
    public static void Test() {
        bool b = new MyBool( true );    // OK
    }
}

所以这似乎只是一个 PowerShell 问题。

(PSVersion: 7.2.2)

Santiago Squarzon 的帮助下,您已经自己完成了大部分发现,但让我尝试总结一下:

您看到两个不同的有问题的 PowerShell 行为:

  • 有问题的行为 A: PowerShell 有自己的内置 to-Boolean 转换逻辑,不幸的是, 没有 支持隐式或显式 .NET 转换运算符。

    • 的底部总结了这个 built-in 逻辑的规则,这解释了为什么它认为 any 你的实例[MyBool] 类型 - 甚至 [MyBool]::new($false) - $true,很不幸。

    • 只有在实例未首先被强制转换为布尔值的操作中才会使用转换运算符,对于大多数运算符而言,这意味着在 LHS[=85= 上使用实例]:

      [MyBool]::new($false) -eq $false # -> $true
      
      [MyBool]::new($false), 'other' -contains $false # -> $true
      
      # With -in, it is the *RHS* that matters 
      $false -in [MyBool]::new($false), 'other' # -> $true
      
    • 相比之下,如果您 强制 一个布尔上下文 - 通过在(通常)LHS 上使用布尔值或使用 implicit to-Boolean 强制 - PowerShell 的 built-in 逻辑 - 不支持转换运算符 - 开始:

      $false -eq [MyBool]::new($false) # -> !! $false
      
      $false, 'other' -contains [MyBool]::new($false) # -> !! $false
      
      # With -in, it is the *RHS* that matters 
      [MyBool]::new($false) -in $false, 'other' # -> !! $false
      
      # Most insidiously, with *implicit* coercion.
      if ([MyBool]::new($false)) { 'what?' } # -> !! 'what?'
      
  • 有问题的行为 B: 当你 type-constrain 一个带有 [bool] 的变量时,即,当您将类型字面量放置在所分配变量的 left 时(例如,[bool] $b = ...,而不是 $b = [bool] (...)[1 ] 绑定 [bool] 参数的规则 - 意外且不恰当地 - 启动,这 - 不同于 any-type-accepted built-in to-Boolean 转换 - 非常严格,如错误消息所示。

    • 也就是说,只有 $true$false 数字 (零映射到 $false 和任何非零$true) 的值可以传递给类型为 [bool].

      的参数
      • 请注意 [bool] 参数本身很少见,因为布尔逻辑是 PowerShell-idiomatically 用 [switch] 参数表示的,当它是 (non-typically) 时一个显式参数,限制性更强,只接受 $true$false
    • 这种有问题的行为 - 不恰当地将 参数 逻辑应用于 (non-parameter) 变量 - 是GitHub issue #10426.

      的主题

[1] 两者的区别在于 type-constraining - [bool] $b = ... - 有效地 locks 在 的数据类型中变量 $b,以便后面分配新值的尝试被强制为同一类型。相比之下,$b = [bool] (...) 仅应用临时 cast 来强制转换,而不会阻止以后的赋值分配具有不同数据类型的值。