Home > other >  Casting object? to a generic type that may or may not be nullable
Casting object? to a generic type that may or may not be nullable

Time:08-30

I'm trying to convert a project to use nullable reference types, but I'm running into an issue. In my project, I have a place where I get an object? that needs to be cast to a generic type T before adding it to a collection. The type T could be anything; a nullable reference type, a non-nullable reference type, or a value type. This isn't known at compile time.

So, let's say I have the following code (toy example):

static T Convert<T>(object? value)
{
    return (T)value;
}

This causes the compiler to complain that value may be null, and that the return of the function may be null. That's fair enough, since if T is non-nullable and value is null, this wouldn't be allowed. I thought maybe this would work:

static T Convert<T>(object? value)
{
    if (value == null)
        return default;
    else
        return (T)value;
}

But this has the same problem: if T is a non-nullable reference type, default is still null, which still violates the constraint.

Making the function return T? is not a solution, because in the case of value types, I don't want to use Nullable<T>.

I thought about throwing an exception if value is null, but I want to allow null if T is nullable. So I'd only want to throw that if T is non-nullable, and that kind of generic specialization doesn't seem possible in C#.

The context here is that I'm using a TypeConverter, and unfortunately the result of conversion is allowed to return null.

Is there a good way to handle this situation?

CodePudding user response:

If you use C# 9.0 or higher, you can use return type of T? without need to resort to Nullable<T>. For generics in non-nullable context there is special set of rules, detailed here.

If the type argument for T is a value type, T? references the same value type, T. For example, if T is an int, the T? is also an int.

You can check GetType() of T with simple console application. If T is int, return type will be System.Int32, not Nullable<System.Int32>.

#nullable enable
using System;

public class Program
{
    public static void Main()
    {
        var result = Convert<int>(null);
        Console.WriteLine(result); // Prints: 0
        Console.WriteLine(result.GetType().FullName); // Prints: System.Int32
    }

    static T? Convert<T>(object? value)
    {
        if (value is null)
            return default(T);
        return (T)value;
    }
}

C# Playground example here.

  • Related