Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

(Non-)Nullable Reference Types in C#

3.08/5 (6 votes)
22 Sep 2024CPOL3 min read 4.8K  
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)