Skip to content

Instantly share code, notes, and snippets.

@AfroThundr3007730
Last active June 29, 2024 21:59
Show Gist options
  • Save AfroThundr3007730/1ca0eb1b6782d891e7aff7897a1101f8 to your computer and use it in GitHub Desktop.
Save AfroThundr3007730/1ca0eb1b6782d891e7aff7897a1101f8 to your computer and use it in GitHub Desktop.
Using PowerShell's AutomationNull to avoid automatic type conversion

PowerShell Type Conversion and AutomationNull

This all started because I wanted to use System.IO.FileNotFoundException in a function. Specifically, this constructor, which allows you to set the error message and the file that caused the exception:

public FileNotFoundException (string? message, string? fileName);

The documentation states that null (the C# null) can be passed as the first parameter to use the default message, which allows me to avoid reinventing the wheel or caring about things like localization of that message. Great.

The problem

PowerShell "helpfully" does automatic type conversion when needed to make any operation work (e.g. 'a' + 2 becomes the string 'a2'). It also does this during parameter binding, meaning my attempt to pass $null (the null automatic variable) results in it getting converted to '' (the empty string) before being bound instead, since PowerShell reads the method signature and assumes a string must be passed as the first parameter.

So this:

[System.IO.FileNotFoundException]::new($null, $InputFile)

becomes this:

[System.IO.FileNotFoundException]::new('', $InputFile)

Great. :/

Note: Were an integer required here, $null would be cast to 0 instead.

The solution

Enter System.Management.Automation.Internal.AutomationNull for the fix.

Powershell uses this for commands that return nothing, to differentiate from, say, a collection of $null on the pipeline. Even though it's supposed to be internal, it can be accessed in several ways. So for example: $aNull = & {} (here $aNull now holds an AutomationNull.Value, not $null).

The nice thing about AutomationNull, is that PowerShell won't convert it like it does with $null, which means we can use it to force a null parameter:

# [System.IO.FileInfo]$InputFile = 'foo.bin'
if (!$InputFile.Exists) {
    throw [System.IO.FileNotFoundException]::new(
        [System.Management.Automation.Internal.AutomationNull]::Value,
        $InputFile)
}

Bit of a mouthful right? You could create $aNull and use it instead, but lets cut out the middle man here and just create an AutomationNull directly:

if (!$InputFile.Exists) {
    throw [System.IO.FileNotFoundException]::new((& {}), $InputFile)
}

And there we go. The FileNotFoundException is called with the first parameter null, and it uses the default message, as we intended. I might wrap that into a new function or exception to save typing later.

Other notes

If you want to detect the difference between $null and AutomationNull.Value, just know that the latter inherits from PSObject, so the following test works:

$pNull = $null # PowerShell null
$aNull = & {}  # AutomationNull

$null -eq $pNull -and $pNull -is [PSObject] # false
$null -eq $aNull -and $aNull -is [PSObject] # true

For further details on the AutomationNull, check out this answer on StackOverflow and this issue on GitHub which goes into detail on why it exists and is in a "pubternal" (internal yet still public) state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment