Type int? vs type int

Nullable types have special "boxing" rules; "boxing" is when a value-type is treated as object, as per your code. Unlike regular value-types, a nullable value-type is boxed either as null (regular null, no type), or as the non-nullable type (the T in T?). So: an int? is boxed as an int, not an int?. Then when you use OfType<int>() on it, you get all the values that are int, which is: the single value you passed in, since it is of type int.


A nullable value type is boxed by the following rules

  • If HasValue returns false, the null reference is produced.
  • If HasValue returns true, a value of the underlying value type T is boxed, not the instance of nullable.

In your example second rule has been followed as you have value:

var i = (object)(int?)123;

It is a bit late, but beside of Marc's answer to your question, I want to give some additional information about Nullable value types in CLR.

The CLR has built-in support for nullable value types. This special support is provided for boxing, unboxing, calling GetType, calling interface methods.

For example, let's check GetType():

Int32? x = 5;
Console.WriteLine(x.GetType());

What you think it will print to the console? System.Nullable<Int32? Not, the result is System.Int32.

Or, let's check boxing, which you noted in your question:

Int32? n =5;
Object o = n;
Console.WriteLine("o's type={0}", o.GetType()); // "System.Int32"

The rule is that:

When the CLR is boxing a Nullable instance, it checks to see if it is null, and if so, the CLR doesn’t actually box anything, and null is returned. If the nullable instance is not null, the CLR takes the value out of the nullable instance and boxes it. In other words, a Nullable with a value of 5 is boxed into a boxed-Int32 with a value of 5.

And, at the end I want to explain how CLR add special support for calling interface methods from Nullable Types. Let's take a look to that:

Int32? n = 5;
Int32 result = ((IComparable) n).CompareTo(5); // Compiles & runs OK
Console.WriteLine(result); // 0

In the preceding code, I’m casting n, a Nullable<Int32>, to IComparable<Int32>, an interface type. However, the Nullable<T> type does not implement the IComparable<Int32> interface as Int32 does. The C# compiler allows this code to compile anyway.