Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to identify a nullable reference type for generic type?

In C# 8 with nullable enabled, is there a way to identify a nullable reference type for generic type?

For nullable value type, there is a section dedicated to it. https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-value-types#how-to-identify-a-nullable-value-type

We are trying to do optional null check according to the generic type

#nullable enable
public static Result<T> Create<T>(T value)
{
   if (!typeof(T).IsNullable() && value is null)
      throw new ArgumentNullException(nameof(value));

   // Do something
}

public static bool IsNullable(this Type type)
{
   // If type is SomeClass, return false
   // If type is SomeClass?, return true
   // If type is SomeEnum, return false
   // If type is SomeEnum?, return true
   // If type is string, return false
   // If type is string?, return true
   // If type is int, return false
   // If type is int?, return true
   // etc
}

So the following will throw ArgumentNullException when T is not nullable But allow value to be null with no exception when T is nullable, e.g.

Create<Anything>(null); // throw ArgumentNullException

Create<Anything?>(null); // No excception
like image 792
Jason Yu Avatar asked Jun 03 '20 04:06

Jason Yu


People also ask

What is a nullable reference type?

Nullable reference types are a compile time feature. That means it's possible for callers to ignore warnings, intentionally use null as an argument to a method expecting a non nullable reference. Library authors should include runtime checks against null argument values.

Are reference types null by default in C#?

Nullable reference types are available beginning with C# 8.0, in code that has opted in to a nullable aware context. Nullable reference types, the null static analysis warnings, and the null-forgiving operator are optional language features. All are turned off by default.

Should you use nullable reference types?

The nullable reference types are super useful. In my case, not surprisingly, it has decreased the amount of null reference exceptions in the code. No doubt that it improves the quality of my code. In the future, I am ready to use it as extensively as I do now and I recommend that you try it too if you have not yet.


2 Answers

In C# 8 with nullable enabled, is there a way to identify a nullable reference type for generic type?

In C# 8 there is NO way to check if a type parameter passed to a generic method is a nullable reference type or not.

The problem is that any nullable reference type T? is represented by the same type T (but with a compiler-generated attribute annotating it), as opposed to nullable value type T? that is represented by the actual .NET type Nullable<T>.

When compiler generates code that invokes a generic method F<T>, where T can be either nullable reference type or not, an information if T is nullable refence type is lost. Lets consider the next sample method:

public void F<T>(T value) { }

For the next invocations

F<string>("123");
F<string?>("456");

compiler will generate the next IL code (I simplified it a bit):

call    F<string>("123")
call    F<string>("456")

You can see that to the second method a type parameter string is passed instead of string? because the representation of the nullable reference type string? during the execution is the same type string.

Therefore during execution it is impossible to define if a type parameter passed to a generic method is a nullable reference type or not.


I think that for your case an optimal solution would be to pass a bool value that will indicate if a reference type is nullable or not. Here is a sample, how it can be implemented:

public static Result<T> Create<T>(T value, bool isNullable = false)
{
    Type t = typeof(T);

    // If type "T" is a value type then we can check if it is nullable or not.
    if (t.IsValueType) 
    {
        if (Nullable.GetUnderlyingType(t) == null && value == null)
            throw new ArgumentNullException(nameof(value));
    }
    // If type "T" is a reference type then we cannot check if it is nullable or not.
    // In this case we rely on the value of the argument "isNullable".
    else
    {
        if (!isNullable && value == null)
            throw new ArgumentNullException(nameof(value));
    }

    ...
}
like image 183
Iliar Turdushev Avatar answered Oct 12 '22 10:10

Iliar Turdushev


You can't use nullable in the constraint, but you can use it in the method signature. This effectively constraints it to be a nullable type. Example:

static Result<Nullable<T>> Create<T>(Nullable<T> value) where T  : struct
{
    //Do something
}

Note that you can use this side by side with your existing method, as an overload, which allows you to execute a different logic path if it's nullable versus if it is not.

static Result<Nullable<T>> Create<T>(Nullable<T> value) where T  : struct
{
    Log("It's nullable!");
    Foo(value);
}

public static Result<T> Create<T>(T value)
{
    Log("It's not nullable!");
    Foo(value);
}
like image 42
John Wu Avatar answered Oct 12 '22 11:10

John Wu