NoNull 1.0.8010.30217
dotnet add package NoNull --version 1.0.8010.30217
NuGet\Install-Package NoNull -Version 1.0.8010.30217
<PackageReference Include="NoNull" Version="1.0.8010.30217" />
<PackageVersion Include="NoNull" Version="1.0.8010.30217" />
<PackageReference Include="NoNull" />
paket add NoNull --version 1.0.8010.30217
#r "nuget: NoNull, 1.0.8010.30217"
#:package NoNull@1.0.8010.30217
#addin nuget:?package=NoNull&version=1.0.8010.30217
#tool nuget:?package=NoNull&version=1.0.8010.30217
NoNull
A .NET library whose purpose is to provide ways to avoid using nulls and null types in C# code.
Why
Sir Tony Hoare stated recognized in a 2009 conference talk that his invention of the null reference in 1965 was a "billion-dollar mistake":
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
In recent years, and in recent versions of C# there has been a push to move developers away from relying on null by providing static anaylsis warnings when a variable may be null to ensure there are appropriate checks. Although some would like this to be taken a step further by changing those warnings to compile-time errors, that would introduce a breaking change for an a large number of existing applications.
Despite the improvements to the static code analysis that gives us more helpful reminders of when something could be null, we are still using null. There is no good built-in construct that gives us a way to avoid null altogether. There are patterns such as the Null Object Pattern which would suggest creating a NullClass for every interface such that a value could either be the actual value or an instance of the NullClass, however this adds a lot of overhead when creating classes and still requires you to think of an implementing what the null behavior should be.
A more generic solution would be preferred that would give us a sensible way to determine when a value exists or not and provide more semantic meaning for these different states.
Inspiration
The inspiration behind this solution is the Result and Option enums provided in Rust. As a modern language, in Rust null does not exist. However, the creators recognized that there still needed to be some concept of a value or no value. To account for this, they created Option which is defined as follows:
pub enum Option<T> {
None,
Some(T),
}
And to cover the case where a response could be either a value or an error, they created the Result enum:
pub enum Result<T, E> {
Ok(T),
Err(E),
}
These two enums provide Rust a generic way to express all response types without the use of null.
The difference
In C# enums are not as powerful as in Rust. For instance, enums cannot hold a generic value, or any non-constant value. Therefore direct translation from Rust to C# is not possible.
We can use other C# types to achieve our goals though. In this case we are using record to allow us to ensure that the data that is set upon creation of the new instance is immutable - we do not want to have the possibity for the value to be made null. We also have data checks within the record constructs such that no value can be set that is null.
While not as simple to implement as the Rust enums, we are able to achieve the same functionality with very minimal code.
You can see the different scenarios of how the Result and Option types can be used to check for values in the unit tests:
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net6.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.8010.30217 | 768 | 12/6/2021 |
| 1.0.8009.34989 | 446 | 12/5/2021 |