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.