In this article, we are going to learn about how we can return null from a generic method in C#.

To download the source code for this article, you can visit our GitHub repository.

Let’s start.

Can We Return null From a Generic Method Directly?

Sometimes, when we create a generic method we might have to suppress exceptions and return null, or returning null might be intentional and part of the design. But whatever the case may be let’s see how we can do that.

We won’t add any logic to our generic method since our focus is on returning null.

Let’s create a generic method and return null directly: 

public static T? FindItem<T>(List<T> items, string id)
{
    return null; // Compiler error
}

This code throws an exception:

Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.

So, why is the compiler unhappy when we return null directly?

The generic parameter T can be a reference type, a nullable value type, or a non-nullable value type. Since we can’t assign null to a non-nullable type the compiler prevents us from doing that.

Return null By Restricting the Generic Type to a Reference Type

Let’s restrict the generic method to accept only reference types as the generic type:

public static T FindItem<T>(List<T> items, string id) where T : class
{
    return null; // No compiler error
}

Now we are able to return null because we can assign null to reference types and we have restricted the generic method to accept only reference type as a generic type.

Return null By Restricting the Generic Type to a Nullable Value Type

Let’s restrict the generic method to accept only value types by restricting T to struct:

public static T FindItem<T>(List<T> items, T id) where T : struct
{
    return null; //Compiler error
}

This code throws an exception as well:

Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.

The compiler doesn’t allow us to return null because we haven’t specified that the generic return type T is nullable:

public static T? FindItem<T>(List<T> items, T id) where T : struct
{
    return null; // No compiler error
}

Returning null here doesn’t return a null reference but the null value of the nullable value type.

null Reference vs null Value

Let’s understand a bit about how a null value differs from a null reference.

Let’s invoke the FindItem with int type:

int? item = FindItem(new List<int> { 1,2,3 }, 4);

Console.WriteLine(item.Value);

We are returning null from our generic method so when we access item.Value it will throw InvalidOperationException instead of NullReferenceException like it does with null reference.

We can still do the null check be it a null reference or a nullable value type like this:

if (item is null)
{
    Console.WriteLine("Item is null");
}

Return null Using the default Keyword

When we can’t restrict the generic type to a reference type or a value type we can’t return null because the compiler doesn’t know whether we are returning a null reference or null value of the nullable value type.

So, how do we return null in this case?

We can use the default keyword:

public static T? FindItemOrDefault<T>(List<T> items, string id)
{
    return default;
}

default returns the appropriate value based on the generic type. If we pass T as nullable value type then it returns the null value of the nullable type, if we pass T as reference type then it returns a null reference.

We have to be careful when we pass a non-nullable value type as a generic type because the return type changes based on the type.

If the type is int then default will return the default value of int which is zero. If it is DateTime then it returns 1/1/0001 12:00:00 AM.

Is Returning null a Bad Practice?

Returning null can be considered bad practice because the caller has to explicitly handle the null checks and forgetting that can lead to unhandled exceptions.

However, if we have to return nulls as part of our design then at least we need to name the methods appropriately like FindItemOrDefault or TryFindItem etc so that the caller can understand that the method might also return null.

If we have to return nulls and use them, we can check the values with the null-coalescing operator and avoid potential NullReferenceExceptions.

We can also enable the nullable feature (.NET 6) so that the compiler can warn us if there is a possible null return.

Conclusion

In this article, we’ve learned how to return null in the generic method when the generic type is a reference type or a value type and how to use default when the generic type can be a value type or a reference type.