Updated 29 Sep 2016: Fixed a bug in IsNull where Bruno Martins pointed out it returned True for 0. This said… this post is super old… and should be ignored.

When you're checking for a null in PowerShell there are 4 different kinds that can crop up (either expectedly or unexpectedly). Here of course the real null:

$a = $null
$a -eq $null
# True

But there's also a .NET type which is a placeholder for the null you'd get when pulling data from a database:

$a = [DBNull]::Value
$a -eq $null
# False
$a -eq [DBNull]::Value
# True

And then there's this when you're looking for null in strings, it might just be an empty string.

[string] $a = $null
$a -eq $null
# False
$a -eq "" -and $a -eq [String]::Empty
# True
if (!$a) {
    Write-Host "True"
} else {
    Write-Host "False"
}
# True

That is insidious because if you write a function that explicitly types its argument as [string] PowerShell will prevent you from passing in $null, always converting it to [String]::Empty.

Which is how the fourth kind of "null" was made. People complained about the above on Microsoft Connect so in PowerShell v4 the team added a special null type for strings:

[string] $a = [System.Management.Automation.Language.NullString]::Value
$a -eq $null
# True
$a -eq "" -and $a -eq [String]::Empty
# False
if (!$a) {
    Write-Host "True"
} else {
    Write-Host "False"
}
# True

Gross. This appears to work for other explicitly declared types too; whatever you assign to it is going to come out the other end as $null. Kind of.

[int] $a = [System.Management.Automation.Language.NullString]::Value
$a -eq $null
# True
if (!$a) {
    Write-Host "True"
} else {
    Write-Host "False"
}
# True

Oddly though if you assign it to an untyped variable it won't be considered a real $null. It takes on the type of the class.

$b = [System.Management.Automation.Language.NullString]::Value
$b -eq $null
# False

if (!$b) {
    Write-Host "True"
} else {
    Write-Host "False"
}
# False

$b.GetType()
# IsPublic IsSerial Name                                     BaseType                                                     
# -------- -------- ----                                     --------                                                     
# True     False    NullString                               System.Object                                                

For the sake of being pedantic here's a function to check for all of them.

function IsNull($objectToCheck) {
    if ($objectToCheck -eq $null) {
        return $true
    }

    if ($objectToCheck -is [String] -and $objectToCheck -eq [String]::Empty) {
        return $true
    }

    if ($objectToCheck -is [DBNull] -or $objectToCheck -is [System.Management.Automation.Language.NullString]) {
        return $true
    }

    return $false
}

IsNull($null)
# True

IsNull($c) # Where $c wasn't declared yet
# True

IsNull([string] $c)
# True

IsNull("")
# True

IsNull([DBNull]::Value)
# True

IsNull(1)
# False

IsNull(New-Object System.DateTime)
# False

IsNull("Looking Good")
# False

IsNull(0)
# False