Description
tl;dr Now that C# 8 is out, people will be using the Nullable Reference Types language feature. We should consider putting the compiler annotations on our library. That would require moving the codebase to C# 8. To reap the benefits in our own codebase, we could also use https://github.com/tunnelvisionlabs/ReferenceAssemblyAnnotator.
What it is
People who put <Nullable>enable</Nullable>
in their projects or #nullable enable
in their source files are then allowed to use the syntax ReferenceType?
where nulls are permitted. In typical software, nulls end up being actually intended in only a small percentage of the usages of reference types.
The compiler then produces warnings (we have /warnaserror) when a thing that might be null is treated as though it can't be null, like when dotting off which could cause an NRE or when passing to a method which could cause an ArgumentNullException, or when setting a field or property which only causes a problem at a later point in time.
This goes a long way toward solving the billion-dollar problem of nullability blindness in the type system.
https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references
https://devblogs.microsoft.com/dotnet/nullable-reference-types-in-csharp/
https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/nullable-reference-types
Things like string.IsNullOrEmpty
are annotated with new compiler attributes such as [NotNullWhen(false)]
on its parameter so that the compiler can recognize that it's a null check. These attributes can be defined by anyone as internal so that they don't conflict with other assemblies. .NET Core 3.0 is the first version of the BCL that adds these attributes as public classes. Referencing https://github.com/tunnelvisionlabs/ReferenceAssemblyAnnotator would define these internally for us without having to maintain the internal attribute definitions ourselves.
Reasons to do it
-
It would be nice to annotate our library so that people who use NUnit are warned if they forget to check things like
TestContext.CurrentContext
for null or if they pass things that could be null to our APIs. It's being up-front about the types that each API accepts in the same way that declaring a parameterIFoo foo
instead ofobject foo
is being more up-front and helpful. -
This would aid the mission of maintaining high-quality internal code written by many different people. We've had our share of null reference exceptions that nothing had forced us to notice at compile time.
-
It's not a breaking change.
Reasons not to do it
-
It requires
<LangVersion>8<LangVersion>
. While Visual Studio wouldn't be required in order to compile the codebase, 2019 Update 3 would become the minimum version if you were using Visual Studio. You could also compile using the .NET Core 3.0 SDK using the command line or using VS Code and other IDEs. -
It's likely to require us to think about things we never had to reconcile before. Once we start shining around this black light, so to speak, we might not like some of the things we see. (Or is this good, as NUnit 4 begins to take shape?)
-
It would touch a decent percentage of our files.
Proposal
I think we should do it in v3 (because this is my function in life, apparently?) and it's work I enjoy and would prioritize. However, I see these downsides and I'm not going to be overly disappointed if we don't want to do it in v3.
I feel more strongly that we should do this by v4. Otherwise, as one of the most-frequently used libraries, I think we'll end up standing out in an unappealing way as time goes on. I could easily be wrong. But if we do this by v4, the easiest way might be to start now in v3 since it can all be done incrementally.