In this article, we are going to learn about how we can return null from a generic method in C#.
Let’s start.
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.
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.
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.
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"); }
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.
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.
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.
When building applications, we try to avoid being tightly coupled to any given third-party service/library,…
In this article, we will explore several different ways to delete elements from an array…
Authorization is a security mechanism that determines a user's access level to a resource. We…
In C#, when we create a new object that is a copy of an existing…
When developing a website, we may want to respond to certain events with specific event…
Issue 166# of the Code Maze weekly. Check out what's new this week and enjoy…