Passing null to a mandatory parameter to a function

后端 未结 4 1311
暗喜
暗喜 2021-01-03 21:18

Effectively my problem comes down to this:

I can\'t have a function with a mandatory parameter and pass $null to that parameter:

Function Foo
{
    P         


        
4条回答
  •  感情败类
    2021-01-03 22:01

    As mentioned in Rohn Edwards's answer, [AllowNull()] and/or [AllowEmptyString()] are part of the solution, but a few things need to be taken into account.

    Even though the example given in the question is with a type string, the question on the title is how to pass null to a mandatory parameter, without a mention of type, so we need to expand the answer slightly.

    First let us look at how PowerShell handles assigning $null to certain types:

    PS C:\> [int] $null
    0
    PS C:\> [bool] $null
    False
    PS C:\> [wmi] $null
    PS C:\>
    PS C:\> [string] $null
    
    PS C:\>
    

    Analyzing the results:

    • Passing $null to an [int] returns an [int] object with value 0
    • Passing $null to a [bool] returns a [bool] object with value False
    • Passing $null to a [wmi] returns ... nothing. It does not create an object at all. This can be confirmed by doing ([wmi] $null).GetType(), which throws an error
    • Passing $null to a [string] returns a [string] object with value '' (empty string). This can be confirmed by doing ([string] $null).GetType() and ([string] $null).Length

    So, if we have a function with a non-mandatory [int] parameter what value will it have if we don't pass that parameter? Let's check:

    Function Foo {
        Param (
            [int] $Bar
        )
        Write-Host $Bar
    }
    
    Foo
    > 0
    

    Obviously if it was a [bool] the value with be False and if it was a [string] the value would be ''

    So when we have a mandatory parameter of most standard types and we assign it $null, we are not getting $null, but rather the "default value" for that type.

    Example:

    Function Foo {
        Param (
            [Parameter(Mandatory = $true)][int] $Bar
        )
        Write-Host $Bar
    }
    
    Foo -Bar $null
    > 0
    

    Notice there is no [AllowNull()] at all there, yet it still returns a 0.

    A [string] is slightly different, in the sense that it doesn't allow empty strings on a mandatory parameter, which is why the example in the question fails. [AllowNull()] doesn't fix it either, as an empty string is not the same as $null and so we need to use [AllowEmptyString()]. Anything else will fail.

    So, where does [AllowNull()] come in play, and how to pass a "real" $null to an int, bool, wmi, etc?

    Neither int nor bool are nullable types, so in order to pass a "real" $null to them you need to make them nullable:

    Function Foo {
        Param (
            [Parameter(Mandatory = $true)][AllowNull()][System.Nullable[int]] $Bar
        )
        Write-Host $Bar
    }
    

    This allows a "true" $null to be passed to an int, obviously when calling Write-Host it converts the $null to a string meaning we end up with an '' again, so it will still output something, but it is a "true" $null being passed.

    Nullable types like [wmi] (or a .NET class) are easier as they are already nullable from the start, so they don't need to be made nullable, but still require [AllowNull()].

    As for making a [string] truly nullable, that one still eludes me, as trying to do:

    [System.Nullable[string]]
    

    Returns an error. Very likely because a system.string is nullable already, though PowerShell doesn't seem to see it that way.

    EDIT

    I just noticed that while

    [bool] $null
    

    gets coerced into False, doing...

    Function Foo {
        Param (
            [bool] $Bar
        )
        $Bar
    }
    
    Foo -Bar $null
    

    throws the following error:

    Foo : Cannot process argument transformation on parameter 'Bar'. Cannot convert value "" to type "System.Boolean".
    

    This is quite bizarre, even more so because using [switch] in the function above instead of [bool] works.

提交回复
热议问题