How can I check whether the return type of a method is nullable or not?
The project setting <Nullable>enable</Nullable>
is active.
Some examples (All methods are of type Task
or Task<T>
)
public sealed class Dto
{
public int Test { get; set; }
}
public sealed class Dto3
{
public int? Test { get; set; }
}
public async Task<Dto?> GetSomething()
{
// This should be found as "Nullable enabled" as the `?` is set.
}
public async Task<Dto3> GetSomething2()
{
// This should not be found as "Nullable enabled" as the `?` is missing.
}
public async Task<List<Dto>> GetSomething3()
{
// This should not be found as "Nullable enabled" as the `?` is missing
// (And the list must be initialized anyways thanks to nullable context).
}
public async Task<List<Dto?>> GetSomething3()
{
// This should not be found as "Nullable enabled" as the `?` is missing in the first generic type after `Task`.
}
I already have code that iterates through the classes and methods in the search namespace like this:
var assembly = Assembly.GetAssembly(typeof(ISomethingService));
if (assembly is null)
{
throw new InvalidOperationException("This should never happen");
}
var classes = assembly.GetTypes().Where(type =>
(type.Namespace == typeof(ISomethingService).Namespace || type.Namespace == typeof(ISomethingService2).Namespace) && type.IsInterface);
foreach (var @class in classes)
{
var methods = @class.GetMethods();
foreach (var method in methods)
{
foreach (var genericType in method.ReturnType.GenericTypeArguments)
{
if (IsNullable(genericType))
{
Console.WriteLine($"Return type of method {@class.Name}.{method.Name} is nullable: {genericType.Name}");
}
}
}
}
The nullable check is currently implemented like this, but doesn't work as expected:
private static bool IsNullable<T>(T obj)
{
if (obj == null) return true; // obvious
Type type = typeof(T);
if (!type.IsValueType) return true; // ref-type
if (Nullable.GetUnderlyingType(type) != null) return true; // Nullable<T>
return false; // value-type
}
Is there a way to achive the desired result (E.g. only having IsNullable
be true
if there is a ?
set on the first level (of generics) after the Task<T>
?
In .NET 6+, you can use NullabilityInfoContext
to find out whether a field, property, parameter, or event is nullable. See also this answer.
In your case, you want to get the method's ReturnParameter
, which is a PropertyInfo
.
bool ReturnNullableOrNullableTask(MethodInfo m) {
var context = new NullabilityInfoContext();
var returnParameter = m.ReturnParameter;
var info = context.Create(returnParameter);
if (info.ReadState == NullabilityState.Nullable) {
return true; // the return type itself is null
}
// otherwise check if it is a Task<T>
if (returnParameter.ParameterType.IsGenericType && returnParameter.ParameterType.GetGenericTypeDefinition() == typeof(Task<>)) {
var firstTypeParameterInfo = info.GenericTypeArguments[0];
if (firstTypeParameterInfo.ReadState == NullabilityState.Nullable) {
return true;
}
}
return false;
}
This works with both nullable value types and nullable reference types.