代码之家  ›  专栏  ›  技术社区  ›  Paul π

非法强制操作根据作用域产生不一致的错误/异常行为

  •  1
  • Paul π  · 技术社区  · 6 月前

    test.ps1

    function MyTestFunction
    {
        [CmdletBinding()]
        param ()
    
        try
        {
            [bool]$functionBool= $null
        }
        catch {throw $_}
    
        Get-Variable 'functionBool'
    }
    
    MyTestFunction -ErrorAction Stop
    
    [bool]$scriptBool= $null
    
    • 上面的例子中有2个非法变量声明( System.Boolean 是不可为null的类型)。



    • 点源采购 .
      Name                           Value
      ----                           -----
      functionBool                   False
      Cannot convert value "" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or 0.
      At C:\scripts\test.ps1:17 char:1
      + [bool]$scriptBool = $null
      + ~~~~~~~~~~~~~~~~~~~~~~~~~
          + CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
          + FullyQualifiedErrorId : RuntimeException
      
      • $functionBool 转化为 $false
        • 未引发异常
      • $scriptBool 转变
        • ArgumentTransformationMetadataException

    • &
      Name                           Value
      ----                           -----
      functionBool                   False
      
      • 对于这两个操作,都不会抛出任何异常。


    • 正如预期的那样,在CLI的全局范围内执行相同的操作会引发异常:

      PS C:\> [bool]$scriptBool = $null
      
      Cannot convert value "" to type "System.Boolean". Boolean parameters accept only Boolean values and
      numbers, such as $True, $False, 1 or 0.
      At line:1 char:1
      + [bool]$scriptBool = $null
      + ~~~~~~~~~~~~~~~~~~~~~~~~~
          + CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
          + FullyQualifiedErrorId : RuntimeException
      

    问题

    • 关于类型强制的作用域和PowerShell的行为,特别是顶部提到的示例,我有什么不理解的?
      • [bool]$functionBool= $null
    • [bool]$functionBool=$null MyTestFunction 功能。
      • 令人困惑的是,删除断点会产生不同的结果,其中 catch{} 块是 执行。
    2 回复  |  直到 6 月前
        1
  •  2
  •   Santiago Squarzon    6 月前

    以下是我们可以推断出的一些事情,首先是对这个问题的一个更简单的解释:

    $sb = { [bool] $var = $null }
    & $sb # no issue
    . $sb # fails
    

    从这里我们可以观察到,当使用以下命令调用脚本块时 & call operator (使用局部范围) PSVariable 从表达式创建的内容没有得到 ArgumentTransformation 与使用调用者的作用域时不同 . dot sourcing operator .

    使用一个没有失败的例子来获取它的实例:

    $sb = { [bool] $var = $true; (Get-Variable var).Attributes }
    & $sb # nothing
    . $sb # transformation attached to it
    

    从这里我们可以看出,这种转变没有能力应对 null :

    $transformation = . $sb
    $transformation.Transform($ExecutionContext, 0)     # False
    $transformation.Transform($ExecutionContext, 1)     # True
    $transformation.Transform($ExecutionContext, $null) # Error!!
    

    实际上,它使用衍生 内部的 类, ArgumentTypeConverterAttribute (see relevant comments in the src) 而且,当经过时 空值 最终击中 this code path ExtendedTypeSystem.resx#L231-L233 :

    else
    {
        bool isNullable = boolType.IsGenericType &&
            boolType.GetGenericTypeDefinition() == typeof(Nullable<>);
    
        if (!isNullable && LanguagePrimitives.IsBooleanType(boolType))
        {
            ThrowPSInvalidBooleanArgumentCastException(null, boolType);
        }
    }
    

    所以,我们可以得出结论,这个问题是由于这种转变而发生的,但如果你足够好奇,下一个问题将是,为什么两者之间存在这种差异 优化 未优化 ScriptBlockDisassembler Module :

    dis

    在我看来,你应该在这里关注的是使用:

    Fake.Dynamic<Func<CallSite, object, bool>>(PSConvertBinder.Get(typeof(bool)))(null)
    

    在顶部的分解表达式中,我敢打赌它最终会使用某种形式的 LanguagePrimitives.ConvertTo<bool>(null) ,其中 已为此转换做好准备 :

    [System.Management.Automation.LanguagePrimitives]::ConvertTo[bool]($null) # False
    

    空值 转换为a bool 甚至在被分配到当地之前( locals ) MutableTuple .

        2
  •  0
  •   Michał Turczyn    6 月前

    评论不允许这么长的文本,所以我会把它放在这里:

    根据我所读到的, CmdletBinding 属性表示函数为 Advanced Function 在这样的函数中,变量和值强制的行为不同,这意味着 $functionBool 在以下情况下,将被评估为具有默认值 $null 提供的价值。

    更重要的是:它的行为类似于“typescript”——空字符串被转换为false,而非空字符串则被转换为true。

    简单地说:value corcion在高级函数中的行为与在常规脚本中的行为不同。

    PS如果你想打印 $functionBool

    推荐文章