NoNull 1.0.8010.30217

dotnet add package NoNull --version 1.0.8010.30217
                    
NuGet\Install-Package NoNull -Version 1.0.8010.30217
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NoNull" Version="1.0.8010.30217" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NoNull" Version="1.0.8010.30217" />
                    
Directory.Packages.props
<PackageReference Include="NoNull" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NoNull --version 1.0.8010.30217
                    
#r "nuget: NoNull, 1.0.8010.30217"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NoNull@1.0.8010.30217
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NoNull&version=1.0.8010.30217
                    
Install as a Cake Addin
#tool nuget:?package=NoNull&version=1.0.8010.30217
                    
Install as a Cake Tool

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:

Result Tests

Option Tests

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • 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