struct constraint in C#

Guriy Samarin
2 min readFeb 22, 2022

--

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 is false, the result of a boxing operation is null. 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 is null. When null is unboxed into a nullable type, the common language runtime creates a new Nullable<T> structure and initializes its HasValue property to false.

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:

--

--

Guriy Samarin
Guriy Samarin

Written by Guriy Samarin

Software developer at Amazon. Web (mostly backend) development now. My stack — .NET (APS.NET Core MVC).

No responses yet