struct constraint in C#
I recently found out that struct constraint in generics is actually struct and not null constraint.
So if you do
public class Class<T> where T : struct
{ }var cl = new Class<int?>(); //you will get compiler error here
//error CS0453: The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Class<T>'
The reason behind it is the special status of Nullable<T>. It’s a value type, but for compiler support, it requires tricks on the CLR side. One of them regarding boxing/unboxing:
When a nullable type is boxed, the common language runtime automatically boxes the underlying value of the Nullable<T> object, not the Nullable<T> object itself. That is, if the HasValue property is
true
, the contents of the Value property is boxed. When the underlying value of a nullable type is unboxed, the common language runtime creates a new Nullable<T> structure initialized to the underlying value.If the
HasValue
property of a nullable type isfalse
, the result of a boxing operation isnull
. Consequently, if a boxed nullable type is passed to a method that expects an object argument, that method must be prepared to handle the case where the argument isnull
. Whennull
is unboxed into a nullable type, the common language runtime creates a new Nullable<T> structure and initializes itsHasValue
property tofalse
.
So Nullable<Nullable<T>> simply not supported.
You can read about it in more detail in this blog. Or you can play with Microsoft example, but using nullable struct as the underlying type
using System;
using MyClass1 = System.Nullable<int>;MyClass1 myClass1 = new MyClass1(1);// Get the RuntimeTypeHandle from an object.
RuntimeTypeHandle myRTHFromObject = Type.GetTypeHandle(myClass1);
// Get the RuntimeTypeHandle from a type.
RuntimeTypeHandle myRTHFromType = typeof(MyClass1).TypeHandle;Console.WriteLine("\nmyRTHFromObject.Value: {0}", myRTHFromObject.Value);
Console.WriteLine("myRTHFromObject.GetType(): {0}", myRTHFromObject.GetType());
Console.WriteLine("Get the type back from the handle...");
Console.WriteLine("Type.GetTypeFromHandle(myRTHFromObject): {0}",
Type.GetTypeFromHandle(myRTHFromObject));Console.WriteLine("\nmyRTHFromObject.Equals(myRTHFromType): {0}",
myRTHFromObject.Equals(myRTHFromType));Console.WriteLine("\nmyRTHFromType.Value: {0}", myRTHFromType.Value);
Console.WriteLine("myRTHFromType.GetType(): {0}", myRTHFromType.GetType());
Console.WriteLine("Get the type back from the handle...");
Console.WriteLine("Type.GetTypeFromHandle(myRTHFromType): {0}",
Type.GetTypeFromHandle(myRTHFromType));
References: