Ryan's answer is correct; to address your many other questions:
How should one read !nullableboolean ?? false
This is the same as (!nullableboolean) ?? false
which is the same as bool? temp = !nullableboolean ... temp.HasValue ? temp.Value : false
It also differs from !(nullableboolean ?? false)
Correct. That would have the semantics of !(nullableboolean.HasValue ? nullableboolean.Value : false)
But then !nullableboolean == null
If nullableboolean
is null then its inverse is also null, correct. The opposite of "I don't know if this is true or false" is "I don't know if this is false or true". You still don't know.
this seems undocumented.
It is not.
And of course !null
doesn't compile.
I do not recall the reason for this or where in the Roslyn code it is determined. Overload resolution could deduce that there are no applicable user-defined operators -- because they are looked up by type, and null has no type -- and that the sole built-in operator is the lifted !
operator.
But a good reason for this exception to the normal rules would be that it's almost always a mistake! If "a null bool?" was intended then (bool?)null
or default(bool?)
or new bool?()
would all be preferable. If it wasn't intended, then the code is wrong. Either way, it can be safely rejected.
If I have a free moment I'll take a look through the code and see if I can recall where this check got implemented.
Maybe it only works in conjunction with ??
No.
So maybe ?? has an undocumented counterpart-operator ! ??
No.
!null ?? false
doesn't compile either.
Correct; again !null
is rejected by some special rule.
Does anybody know of some explanation or documentation?
Read the specification. My articles on how I wrote the nullable arithmetic optimizer might also be of interest to you:
https://ericlippert.com/2012/12/20/nullable-micro-optimizations-part-one/