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
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:
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:
$null
to an [int]
returns an [int]
object with value 0
$null
to a [bool]
returns a [bool]
object with value False
$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$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.
I think you missed the "," to separate the parameters.
Param (
[string]$ObjectName,
[int]$ObjectId,
[wmi]$Object
)
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".