C#
System.Type
nullable types
type conversion
.NET

How do I convert a System.Type to its nullable version?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When you work with reflection in C#, you often have a System.Type value instead of a compile-time generic type. Converting that type to its nullable form is straightforward once you remember one important rule: only non-nullable value types can become Nullable<T>.

What "Nullable Version" Really Means

In .NET, Nullable<T> is valid only for value types such as int, DateTime, and bool. Reference types already support null at runtime, so there is no separate runtime wrapper type for "nullable string" or "nullable object".

That means a conversion helper should behave like this:

  • if the input is a non-nullable value type, return Nullable<T>
  • if the input is already nullable, return it unchanged
  • if the input is a reference type, return it unchanged

This matches the rules enforced by the runtime.

A Reflection Helper

The standard way to build Nullable<T> dynamically is typeof(Nullable<>) plus MakeGenericType.

csharp
1using System;
2
3public static class TypeExtensions
4{
5    public static Type ToNullableType(this Type type)
6    {
7        if (type == null)
8        {
9            throw new ArgumentNullException(nameof(type));
10        }
11
12        if (!type.IsValueType)
13        {
14            return type;
15        }
16
17        if (Nullable.GetUnderlyingType(type) != null)
18        {
19            return type;
20        }
21
22        return typeof(Nullable<>).MakeGenericType(type);
23    }
24}

This helper is safe for the common cases:

csharp
1Console.WriteLine(typeof(int).ToNullableType());        // System.Nullable`1[System.Int32]
2Console.WriteLine(typeof(int?).ToNullableType());       // System.Nullable`1[System.Int32]
3Console.WriteLine(typeof(string).ToNullableType());     // System.String
4Console.WriteLine(typeof(DateTime).ToNullableType());   // System.Nullable`1[System.DateTime]

The logic is intentionally small because the rules themselves are small.

Detecting Existing Nullable Types

Nullable.GetUnderlyingType(type) is the cleanest test for whether a type is already Nullable<T>. It returns the underlying T for nullable value types and null otherwise.

csharp
1Type type1 = typeof(int?);
2Type type2 = typeof(int);
3
4Console.WriteLine(Nullable.GetUnderlyingType(type1)); // System.Int32
5Console.WriteLine(Nullable.GetUnderlyingType(type2)); // null

That method is better than manually checking generic type definitions because it directly expresses the intent.

Generic and Reflection-Driven Scenarios

This conversion comes up most often in infrastructure code:

  • building dynamic forms from model metadata
  • mapping database column types to property types
  • generating expression trees
  • creating validation rules from reflected members

For example, a mapper may want to treat database columns as optional when a source schema says a field can be missing.

csharp
1Type propertyType = typeof(int);
2Type runtimeType = propertyType.ToNullableType();
3
4object? value = null;
5Console.WriteLine(runtimeType); // System.Nullable`1[System.Int32]

Without the conversion, reflective code may fail when it tries to assign null to a non-nullable value type.

Nullable Reference Types Are Different

Modern C# also has nullable reference type annotations such as string?, but those are mostly compile-time annotations, not different runtime Type objects. Reflection sees both string and string? as System.String.

That is why the helper above returns reference types unchanged. There is no runtime Nullable<string> to construct, and trying to build one would be invalid.

If you need nullable reference metadata, that is a separate reflection problem involving attributes and nullability context, not Nullable<T>.

Common Pitfalls

Calling typeof(Nullable<>).MakeGenericType(type) on a reference type throws an exception because Nullable<T> requires a value type.

Wrapping an already nullable type again is unnecessary and invalid. Check Nullable.GetUnderlyingType first.

Confusing nullable value types with nullable reference types leads to incorrect reflection logic. They are handled very differently at runtime.

Forgetting that enums are value types is another common oversight. Enums can be converted to nullable forms just like int or bool.

Skipping the null check on the incoming Type argument makes helper code fail with a less useful exception later.

Summary

  • Only non-nullable value types can be converted to Nullable<T>.
  • Use Nullable.GetUnderlyingType to detect whether a type is already nullable.
  • Build the runtime wrapper with typeof(Nullable<>).MakeGenericType(type).
  • Return reference types unchanged because they are already nullable at runtime.
  • Do not confuse runtime nullable value types with compile-time nullable reference annotations.

Course illustration
Course illustration

All Rights Reserved.