I'm curious as to why the following code works (run under the VS debugger):
int? x = null;
null
x.HasValue
false
If x
is indeed null, what instance does HasValue
refer to? Is HasValue
implemented as an extension method, or does the compiler special case this to make it magically work?
Java primitive types (such as int , double , or float ) cannot have null values, which you must consider in choosing your result expression and host expression types.
As you know, a value type cannot be assigned a null value. For example, int i = null will give you a compile time error. C# 2.0 introduced nullable types that allow you to assign null to value type variables.
Nullable types are neither value types nor reference types.
Because x
isn't a reference type. The ?
is just syntactic sugar for Nullable<T>
, which is a struct
(value type).
int?
is actually a structure Nullable<int>
. Hence this, your x
cannot be null, because it is always instance of a structure.
Hand-waving answer: Nullable structs are magic.
Longer answer: Null is not actually what is represented by the value. When you assign null to a nullable struct, what you will see happen behind the scenes is different.
int? val = null; // what you wrote
Nullable<Int32> val = new Nullable<Int32>(); // what is actually there
In this case, an instance of the struct is created that has the T Value
set to a default value and the bool HasValue
property set to false.
The only time you will actually obtain a null reference from a Nullable<T>
is when it is boxed, as a Nullable<T>
boxes directly to T
or null, depending upon if the value is set.
There are several meanings to null
.
One in programming languages which present variables and memory in a pointer-based manner (which includes C#'s references though it hides some of the details) is "this doesn't point to anything".
Another is "this has no meaningful value".
With reference types, we often use the former to represent the latter. We might use string label = null
to mean "no meaningful label. It remains though that it's still also a matter of what's going on in terms of what's where in memory and what's pointing to it. Still, it's pretty darn useful, what a shame we couldn't do so with int
and DateTime
in C#1.1
That's what Nullable<T>
provides, a means to say "no meaningful value", but at the level below it's not null
in the same way a null
string is (unless boxed). It's been assigned null and is equal to null so it's logically null and null according to some other semantics, but it's not null in the "doesn't point to anything" implementation difference between reference and value types.
It's only the "doesn't point to anything" aspect of reference-type null that stops you from calling instance methods on it.
And actually, even that isn't strictly true. IL let's you call instance methods on a null reference and as long as it doesn't interact with any fields, it will work. It can't work if it needs (directly or indirectly) those fields since they don't exist on a null refernce, but it could call null.FineWithNull()
if that method was defined as:
int FineWithNull()
{
//note that we don't actually do anything relating to the state of this object.
return 43;
}
With C# it was decided to disallow this, but it's not a rule for all .NET (I think F# allows it, but I'm not sure, I know unmanaged C++ allowed it and it was useful in some very rare cases).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With