Click here to Skip to main content
16,012,061 members
Articles / Programming Languages / C#

(Non-)Nullable Reference Types in C#

Rate me:
Please Sign up or sign in to vote.
3.08/5 (6 votes)
22 Sep 2024CPOL3 min read 3.8K   1   12
This post is mostly a complaint about what could be a great feature that actually became a source of security vulnerabilities.

Background

Before starting, I must say that I am a great supporter of safety-features in programming languages, and I had proposed what I called the "required" keyword for C# more than 10 years ago. Yet, I keep seeing more and more people talking how great nullable reference types are in C# and, well, I am really unpleased on how badly the feature was implemented into the language, and this post is all about it.

(Non-)Nullable Reference (Types)

So, there are three things that I don't like in nullable reference types in C#:

  1. They are about non-nullable variables;
  2. They are not types;
  3. They might make your code vulnerable to attacks.

If those brief descriptions aren't enough, let me expand on each one of them:

1. They are about non-nullable variables

Calling the new feature "nullable reference types" commits two mistakes at once. In C# (and .NET) references were always nullables. What the new feature tried to add was non-nullable references (types). Well, the "types" part is what item number 2 is all about.

2. They are not types

Non-nullable references only exist at variable declarations, be them local variables, method parameters, return "types" (not sure the best word to use here) and fields. Yet, as a type, they are still the full nullable type. Although when writing them they might look the same as nullable vs non-nullable value types, non-nullable references still use the nullable types, while the actual types for nullable and non-nullable value types are different (like, Nullable<int> and just int).

3. They might make your code vulnerable to attacks

The entire purpose of adding non-nullable references is to avoid mistakes where null values enter places where they shouldn't. Yet, the false security provided by (non-)nullable references is doing exactly the opposite. Many developers (be it by themselves, because of the tools they use or by practices enforced by the company where they work) are systematically removing null validations from their code, after all they are using non-nullable references. Unfortunately, non-nullable references can still be null and, if they aren't properly checked, nulls might enter data-structures that shouldn't allow null and cause errors to happen later. This actually introduces security vulnerabilities to components shared among different modules and threads, and makes the overall debugging experience harder, which is quite the opposite of what the feature was supposed to do.

How things could be better?

In concept, very simple. Non-nullable reference types should be their own types. They should be seen as distinct types from their nullable counterparts, possibly following the same pattern of nullable and non-nullable value-types, allowing even overloading between the nullable and non-nullable ones, and guaranteeing that the IL itself would never allow a non-nullable reference type to actually contain null, very similar to how C++ references work, as trying to cast a nullptr to a reference will cause an immediate error.

Conclusion

The conclusion here goes to two different ways. The first is that non-nullable references are just a half-backed feature on its current implementation. I am not saying you should not use it, but be aware of its pitfalls.

This gets to the second conclusion, which is to keep validating your input values, even when they are not-supposed to be null, especially if you write shared libraries that might be used outside of your project, by other developers that might not be using the non-nullable references in their own projects.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions

 
QuestionClarification Pin
Niemand2527-Sep-24 7:03
professionalNiemand2527-Sep-24 7:03 
AnswerRe: Clarification Pin
Paulo Zemek27-Sep-24 9:47
Paulo Zemek27-Sep-24 9:47 
QuestionExample Pin
star__duster26-Sep-24 18:56
star__duster26-Sep-24 18:56 
AnswerRe: Example Pin
Paulo Zemek27-Sep-24 9:53
Paulo Zemek27-Sep-24 9:53 
PraiseRe: Example Pin
star__duster28-Sep-24 0:14
star__duster28-Sep-24 0:14 
GeneralRe: Example Pin
Paulo Zemek28-Sep-24 8:18
Paulo Zemek28-Sep-24 8:18 
QuestionHow does this create a security vulnerability? Pin
David Pierson23-Sep-24 17:31
David Pierson23-Sep-24 17:31 
AnswerRe: How does this create a security vulnerability? Pin
Paulo Zemek23-Sep-24 17:36
Paulo Zemek23-Sep-24 17:36 
It is about the false-safety.
When a variable clearly can be null, people will do null checks at input. When a reference is non-nullable, such a check is often omitted (it is even a thing that some static analysis tools will mark as a redundant check).
Unfortunately, non-null references can still be null (this is even more problematic on DLLs that might be used by older versions of C# that will not even warn the users).
So, if you are writing library code and a tool tells you that a null check is unnecessary, it is wrong.

And the null checks that do happen are at the time of dereferencing the variable... if such a value (null) is put inside a list, for example, it is when iterating the list (or similar) that the exception will happen, not when adding the value to the list. (which is the entire reason why we check for null and throw ArgumentNullExceptions).

QuestionMarking a reference as not nullable does not make it more vulnerable to attacks Pin
Peter Huber SG23-Sep-24 14:19
mvaPeter Huber SG23-Sep-24 14:19 
AnswerRe: Marking a reference as not nullable does not make it more vulnerable to attacks Pin
Paulo Zemek23-Sep-24 17:32
Paulo Zemek23-Sep-24 17:32 
GeneralComment Pin
Brandon Kothell22-Sep-24 19:44
Brandon Kothell22-Sep-24 19:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.