Reputation: 319
I've tried this code in PowerShell ISE and VS Code with the same strange result. Without a breakpoint, the output is EMPTY
, but with a breakpoint in the line with "NULL"
, the output is NULL
(as expected). Why?
function demo {
param(
[string] $value = [NullString]::Value
)
if ($null -eq $value) {
"NULL"
} elseif ($value -eq '') {
"EMPTY"
} else {
"$value"
}
}
demo
I know now, that PowerShell will always convert a non-string value (e.g. $null or [NullString]::Value) to an (empty) string, when you use the type modifier [string] for a parameter. Fine, I can live with that, but it's hard to figure that out by yourself, if debugging is so weird in this case.
Upvotes: 6
Views: 859
Reputation: 440162
PetSerAl, as many times before, has provided the crucial pointer in a comment on the question:
A known optimization bug is the most likely cause (as of Windows PowerShell v5.1 / PowerShell Core v6.1.0), and that bug just happens to be masked when the code is run in the Visual Studio Code or ISE debugger.
You can therefore use the same workaround mentioned in the linked bug report: place a call to Remove-Variable
anywhere in the function body (its presence is enough - the call needn't actually be made at runtime):
function demo {
param(
[string] $value = [NullString]::Value
)
# Workaround for optimization bug
if ($False) { Remove-Variable }
if ($null -eq $value) {
"NULL"
} elseif ($value -eq '') {
"EMPTY"
} else {
"$value"
}
}
demo
Now you consistently get "NULL"
as the output, whether debugging or not.
However, it's best to restrict use of [NullString]::Value
to what it was designed for: passing null
to string
-typed parameters of .NET methods - see below.
As for why use of [NullString]::Value
is needed at all in order to pass $null
to a string parameter / store $null
in a [string]
variable, given that .NET strings can normally store null
($null
) directly:
By (historical) design, PowerShell converts $null
to ''
(the empty string) when you assign it to a [string]
variable; here's the rationale:
From https://github.com/PowerShell/PowerShell/issues/4616#issuecomment-323530442:
The thinking behind the design was that in most ways,
$null
and the empty string both represent the same error condition and that in the rare case where a distinction was important,$PSBoundParameters
would be sufficient to distinguish between knowing a value was provided or not.
Given that even passing $null
directly performs conversion to ''
when passing arguments to string
-typed .NET methods, you couldn't pass null
to such methods up to v2.
To remedy that, version 3 introduced [NullString]::Value
, which explicitly signals the desire to pass $null
in a string context.
(The alternative - making PowerShell strings default to $null
and allowing direct assignment of $null
- was considered a change that would break too many existing scripts.)
Using [NullString]::Value
beyond its intended purpose - i.e., for passing null
to string
parameters in .NET methods - is problematic, given that PowerShell doesn't expect [string]
variables to contain $null
in other contexts.
Fixing the above-mentioned optimization bug would help in the scenario in the question, but there may be other pitfalls.
Upvotes: 3
Reputation: 16116
Difference between $null and ""
Empty string is not the same as null; you'd need to test specifically for that. Null is the non-existence of an object, Whereas a string is an object even if it's empty.
That's a common gotcha in PowerShell. For whatever reason, it won't let you assign a $null value to a string variable (or a string parameter to a .NET type); it gets converted to an empty string. This can cause some headaches when calling .NET methods that treat null and empty strings differently, which is why they later added (in v3, if I remember correctly), the [System.Management.Automation.NullString] class. If you want to call such a method, you do this:
[SomeClass]::SomeMethod([nullstring]::Value)
[string]$camp = $null
Will assign $Null to $Camp, but since the [string]-part forces $camp to be of the type string, $Camp will be assigned the value of [String]$Null
[String]$Null will force a conversion of $Null (which is basically a non-existing object) to a string and in PowerShell that results in an empty string.
As far as PowerShell is concerned, that's basically correct. However, in the .NET Framework, strings really can be null, and there's a difference between a null string reference and an empty string. Sometimes that causes confusion when you're looking at .NET documentation and wondering why it doesn't seem to work properly from PowerShell.
Use [string]::IsNullOrEmpty($variable)
https://powershell.org/forums/topic/difference-between-null-and/
See also...
How can I check if a string is null or empty in PowerShell? [string]::IsNullOrEmpty(...)
* Update *
Changing your code to this...
function demo {
param(
[string]$value
)
if ([string]::IsNullOrEmpty($value))
{
"NULL"
} elseif ($value -eq '') {
"EMPTY"
} else {
"$value"
}
}
With or without debug (breakpoint set on 'NULL' and 'EMPTY') effort, the results returned are the same on my system ISE/VSCode
# Results
demo
NULL
demo -value ''
NULL
demo -value ' '
demo -value test
test
* Modification to show the elseif handling whitespace *
With or with the conditions previously stated
function demo {
param(
[string]$value
)
if ([string]::IsNullOrEmpty($value))
{
"NULL"
} elseif ([string]::IsNullOrWhiteSpace($value)) {
"EMPTY or a White Space"
} else {
"$value"
}
}
# Results
demo
NULL
demo -value ''
NULL
demo -value ' '
EMPTY or a White Space
demo -value test
test
Upvotes: 0