Sometimes, when the compiler tries to help, it actually hurts.

No bovine excrement. Really. Nullable<> is a struct. What that means (idea-wise) is that the bytes that represent the value is on the so-called stack. For Reference types, the value on the stack, is a memory address. What that means is structs are never really null. Null for a reference type is like all zeroes. It points to nothing. And means it was never set, or intentionally un-used. It’s supposed to be a memory address that once you examine it, it is the start (at the very least) of the type of object. Structs on the other hand, since all zeroes is usually a valid possible value, is not intuitively interpreted as null. All zeroes interpreted as a “null reference”, is not a “invalid value”, the computer is asked to interpret it as a integer (it is just the number 0, then).

When the compiler looks sees code like int?.GetType(), it likely tries to find a method corresponding to .GetType(object) because everything in .NET needs to support .GetType() and probably creates IL code analogously to .GetType(new ObjectBoxForint?(int?)), and that the runtime evaluation of a int? with int?.HasValue=false probably looks in a general sense to new ObjectBoxForint?(int?), but in execution, evaluates to null (probably all zeroes). You can probably confirm some of this by decompiling, and taking a look at the IL, produced by below. But you can observe the run-time behavior of the code below. There should be a NullReferenceException caught.

static void Main(string[] args)
{
    int? myint = null;
    if (!myint.HasValue)
        Console.WriteLine("Not null reference obviously, since calling a Property on a null reference will usually cause exception");

    object boxed = myint;
    if(boxed==null)
        Console.WriteLine("No conservation of information here.  The compiler doesn't know that obj is actually a int?, so it has to call the static method for ==");

    try
    {
        boxed.GetType();
    }
    catch
    {
        Console.WriteLine("Seriously, nullable types get casted to null, so you can't call GetType().");
    }
/*
because it's looking for the type at the address for boxed, but null or probably zero isnt a real memory address.  it needs to know what type it is, so it can find the GetType() for int?.  This is what is known as a virtual method.  It needs to take that extra hop to verify the type.  THat's how overriding works.  Otherwise the compiler might assume that the same method needs to be run on the same variable type, despite the reference might be pointing to a subclass.  I can test this when it's important, but I'm guessing that's why the compiler tells you about the new keyword on a method, hiding the earlier implementation.  It's possible the new keyword method won't always be called from a ancestor declared variable.  Only when the type is declared exactly.
*/
}

The compiler will box Nullable<> into an object reference, as null, when the .HasValue is false. Which is relevant, if there is any difference between the way you plan to treat int? = null, or string = null.

Not null reference obviously, since calling a Property on a null reference will usually cause exception

No conservation of information here. The compiler doesn't know that obj is actually a int?, so it has to call the static method for ==
Seriously, nullable types get casted to null, so you can't call GetType().

If you’re thinking “so what”, that means you don’t fully understand. If we tried to box int instead, the compiler again looks for a method corresponding to .GetType(object) but this time creates IL code like: .GetType(new ObjectBoxForint(int)), and that value for new ObjectBoxForint(int) is NEVER expected to be evaluated to null. It is always pointing to an object reference called a “Boxed Int” that stored the integer value within (may be 0). But knowing that Nullable<> is a struct, and structs need to be “boxed” or put into a memory structure in main memory(on the heap?). This tends to make most developers believe that boxing an struct, will never produce a NullReference exception.

How I think boxing in C# structures value types in memory, which includes structs and the Nullable<> struct which is syntactically assisted by the compiler. Or at least this mental imagery hasn’t failed me yet, and I’m too lazy to verify it in a book.

Fortunately, this is only a problem, if you are the argumentative type. Most people are going to think if (object obj==null) abortcode(); should work the same way, if the obj contains a object reference, or a boxed int?. If you think about it, when you actually write the code and obj can contain Nullable<> or reference types, most of the time, the code will look just like that.

Leave a Reply

Your email address will not be published. Required fields are marked *