With the Christmas Holidays after us, I finally got some time to start upgrading some older .NET Core applications to .NET 9. First thing I noticed after upgrading is that I got some new warnings related to the ‘notnull’ constraint.
The ‘notnull’ constraint was introduced as part of the nullable reference types feature in C#. The goal of nullable reference types was to solve the ‘million dollar mistake’ and help you to prevent common issue of null reference exceptions. They allow to explicitly state whether a reference type variable can be null or not, helping to catch potential null dereferences at compile time.
The feature is in fact a combination of multiple elements all with the goal of avoiding unexpected null reference exceptions:
Nullable and Non-Nullable Reference Types:
-
Non-Nullable: If you declare a reference type without the
?
suffix (e.g.,string name
), it is considered non-nullable. The compiler issues warnings if you try to assign a null value to it. -
Nullable: If you declare a reference type with the
?
suffix (e.g.,string? name
), it can hold a null value. The compiler warns you when you might dereference it without a null check.
Null-State Analysis: The compiler tracks the null-state of each variable, which can be either not-null
or maybe-null
. It issues warnings if there's a possibility of a null dereference.
Annotations and Attributes: You can use various attributes (like [NotNullWhen(false)]
) and annotations to give the compiler more information about the nullability of parameters and return values in methods.
As part of this feature a new generic constraint was introduced, the ‘notnull’ constraint. This guarantees that the provided type is a non-nullable type. This can either be a non-nullable reference type or a non-nullable value type.
Remark: As this feature was introduced already in C# 8.0, I think it is just a coincidence I noticed it only after upgrading to .NET 9. One extra reason to enable the ‘Treat warnings as errors’ flag…
I got 2 places where I got this warning:
ModuleExtensions.cs(83,137,83,144): warning CS8714: The type 'T' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'T' doesn't match 'notnull' constraint.
NoOpLogger.cs(15,24,15,34): warning CS8633: Nullability in constraints for type parameter 'TState' of method 'NoOpLogger.BeginScope<TState>(TState)' doesn't match the constraints for type parameter 'TState' of interface method 'ILogger.BeginScope<TState>(TState)'. Consider using an explicit interface implementation instead.
Here is each time the original code and the fix:
Original code:
Fix:
Original code:
Fix: