The reason this works:
test_M_NoE [string]$x
Is that [string]$x is not being interpreted the way you expect.
Let's change your test function definition to help us better see what's actually going on:
function test_M_NoE {
param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$x
)
Write-Host "Argument value passed was: '$x'"
}
Now, let's try again:
PS ~> $x = $null
PS ~> test_M_NoE [string]$x
Argument value passed was: '[string]'
Aha! The argument expression [string]$x did not result in an empty string - it resulted in the literal string value [string].
This is due to the fact that PowerShell attempts to parse command arguments differently from anything else. From the about_Parsing help topic:
Argument mode is designed for parsing arguments and parameters for commands in a shell environment. All input is treated as an expandable string unless it uses one of the following syntaxes: [...]
So really, PowerShell interprets our argument expression like a double-quoted string:
test_M_NoE "[string]$x"
At which point the behavior makes sense - $x is $null, so it evaluates to an empty string, and the result of the expression "[string]$x" is therefore just [string].
Enclose the argument expression in the $(...) subexpression operator to have it evaluated as a value expression instead of as an expandable string:
test_M_NoE $([string]$x)
$nullto[string]in PowerShell results in an empty string (eg.""or[String]::Empty), so the argument passed is never actually$nullonce you cast to[string]. But in your case,[string]$xis not an actual cast expression - it's parsed in argument mode, and interpreted as an expandable string, eg. no different thattest_M_NoE "[string]$x""[string]$null"results in a non-empty string