Where in memory are nullable types stored?
Solution 1:
First off, Nullable<int>
is just a shorthand for something like:
struct Nullable<T>
{
bool hasValue;
T value;
}
Plus all the constructors, accessors, and so on. That's all it is -- a nullable int is an ordinary int plus a flag that says whether the int is null or not. All the rest is compiler magic that treats "null" as a valid value; all "null" does with a nullable type is makes you one of those structs with the flag set to false.
So now that we have that out of the way, your question is "where do they go in memory"? They go the same place that any other structs go in memory: where the runtime and compiler believe to be the best place given the lifetime of the memory.
Most structs go on the heap. Anyone who tells you that "structs always go on the stack" doesn't actually know what they are talking about; our documentation does not say that and it is not true. Structs only go on the temporary memory pool, aka "the stack", when they are local variables or temporaries, and the local variables are not closed-over outer variables of an anonymous method or lambda, and the local variables are not in an iterator block. All other structs go on the heap in our implementation.
Note also that there is no requirement whatsoever that an implementation of the CLI use "the stack" to make their temporary pool. The classic JScript memory manager, for example, stores its temporary pool on the heap. (Though of course the JScript runtime engine is not an implementation of the CLI; I'm merely pointing out that one can design a managed runtime engine that puts no user data whatsoever on "the stack".) Logically it is a stack data structure, but that data structure is not store on "the" stack, it's just a stack structure allocated on the heap.
I have to ask: why do you care? The CLR manages memory on your behalf. Why do you care where nullable types go? They go where they live long enough to be useful to you; you don't have to worry about that.
Solution 2:
See Eric's comment about where structs are stored. It is much more thorough (and correct) than what I posted.
The way it works is that the nullable has a boolean that says whether or not the value has been set. Technically you are correct the nullable (int, bool) etc isn't actually null. it has a default value. it's just that now the nullable type has a boolean you can check and see if that value was set or it is just it's default value. The =
and ==
are overridden to set the appropriate values when assigning null
(and a value) to it
MSDN link to Nullables
Another link describing how nullables work:
http://www.markzhou.com/blog/post/2010/01/27/Why-can-assign-e2809cnulle2809d-to-nullable-types.aspx
Solution 3:
Nullables only pretend to be null:
int? a = null;
Debug.Assert((a == null) == (!a.HasValue));
The compiler is complicit in this charade. One funny consequence of this is the following:
Nullable<double> b = new Nullable<double>();
Debug.Assert(b == null); //I'm null upon construction!
They also get special boxing support:
int? c = null;
Object d = c;
Debug.Assert(d == null);
Debug.Assert(!c.HasValue);
Solution 4:
In addition to Kevin's answer: the compiler knows about Nullable types and changes an ==null
check into a call to ".HasValue".