Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nullable properties vs. Nullable local variables

I am puzzled by the following behavior of Nullable types:

class TestClass {
    public int? value = 0;
}

TestClass test = new TestClass();

Now, Nullable.GetUnderlyingType(test.value) returns the underlying Nullable type, which is int. However, if I try to obtain the field type like this

FieldInfo field = typeof(TestClass).GetFields(BindingFlags.Instance | BindingFlags.Public)[0];

and I invoke

Nullable.GetUnderlyingType(field.FieldType).ToString()

it returns a System.Nullable[System.Int32] type. So that means the method Nullable.GetUnderlyingType() has a different behavior depending on how you obtain the member type. Why is that so? If I simply use test.value how can I tell that it's Nullable without using reflection?

like image 383
nickolayratchev Avatar asked May 05 '13 05:05

nickolayratchev


1 Answers

smartcaveman's answer is the best one here so far in that it actually identifies the section of the documentation that describes this behaviour.

The behaviour is undesirable and unfortunate; it is due to the behaviour in combination of three features which, by themselves, behave reasonably.

The three features are:

  • GetType is a non-virtual method; it cannot be overridden. This should make sense; an object doesn't get to decide what its type is reported as. By making it non-virtual, the method is guaranteed to tell the truth.

  • The this value passed to a non-virtual method declared on object must be converted to object; therefore in the case of objects of value type, the receiver of the call to GetType() is boxed to object.

  • Nullable value types have no boxed form; when you box a nullable int, you either get a boxed int or you get a null reference. You never get a valid reference to a boxed nullable int.

Each feature is reasonable on its own but in combination the result is undesirable: when you call GetType on a valid nullable int, the runtime boxes the nullable int to a boxed int and then passes that as the this of object.GetType which of course reports int. If the value is a null nullable int, the runtime boxes to null and then invokes GetType on a null reference and crashes. Neither of these behaviours are desirable, but we're stuck with them.

like image 167
Eric Lippert Avatar answered Oct 16 '22 23:10

Eric Lippert