Passing null to a mandatory parameter to a function

后端 未结 4 1289
暗喜
暗喜 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:00

    You should be able to use the [AllowEmptyString()] parameter attribute for [string] parameters and/or the [AllowNull()] parameter attribute for other types. I've added the [AllowEmptyString()] attribute to the function from your example:

    Function Foo {
        Param
        (
            [Parameter(Mandatory = $true)]
            [AllowEmptyString()]  <#-- Add this #>
            [string] $Bar
        )
    
        Write-Host $Bar
    }
    
    Foo -Bar $null
    

    For more info, check out the about_Functions_Advanced_Parameters help topic.

    Be aware that PowerShell will coerce a $null value into an instance of some types during parameter binding for mandatory parameters, e.g., [string] $null becomes an empty string and [int] $null becomes 0. To get around that you have a few options:

    • Remove the parameter type constraint. You can check for $null in your code and then cast into the type you want.
    • Use System.Nullable (see the other answer for this). This will only work for value types.
    • Rethink the function design so that you don't have mandatory parameters that should allow null.
    0 讨论(0)
  • 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.

    0 讨论(0)
  • 2021-01-03 22:08

    I think you missed the "," to separate the parameters.

    Param (
        [string]$ObjectName,
        [int]$ObjectId,
        [wmi]$Object
    )
    
    0 讨论(0)
  • 2021-01-03 22:11

    First, add an AllowNull attribute:

    Function Foo
    {
        Param
        (
            [Parameter(Mandatory = $true)][AllowNull()][string] $Bar
        )
    
        Write-Host $Bar
    }
    

    Then, when you call the function, be aware that $null when used for [string] becomes an empty string, not a null reference. Therefore (since PowerShell 3 (2012)) use [NullString]::Value like this:

    Foo -Bar ([NullString]::Value)
    

    See the documentation for System.Management.Automation.Language.NullString class.


    EDIT: From the call site this appears to work, but at soon as you reach the Write-Host cmdlet within the function, $Bar will again have become an empty string. See comments below. We must conclude that PowerShell insists strings must be like that. Note that NullString (which has been useful to me in other cases) in its documentation only promises to be helpful when passing null "into a .NET method".

    0 讨论(0)
提交回复
热议问题