C# Nullable Reference Types: IntelliSense Confusion
Tags: dotnet, dotnetcore, csharp, visualstudio
Published: 2020-02-13 21:29:30

---
title: C# Nullable Reference Types: IntelliSense Confusion
published: true
date: 2020-02-13 16:49:00 UTC
tags: dotnet, dotnetcore, csharp, visualstudio
canonical_url: https://sebnilsson.com/blog/c-nullable-reference-types-intellisense-confusion/
---

The feature and concept of _[Nullable reference types](https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references)_ were introduced in C# 8.0 and it basically **made all types non-nullable by default** and ensured that these types could never be assigned the value `null`. This is one of my favorite features in C# recently, but there are scenarios where **a mixed nullable environment could cause confusion**.

![Confusion](https://files.sebnilsson.com/web/images/nullable-confusion/confusion2.jpg)

To enable the assignment of the value `null` to a type, **you have to explicitly mark that type**. This uses the same concept of nullable as introduced in C# 2.0, where you, for example, make an `int` nullable by adding a question mark after it: `int?`.

When we look at a regular example service-class, we can see which benefits can be had from _Nullable reference types_:

```
public class ProductService
{
    // This method accepts a non-null string for 'productId'
    // and always returns a string
    public string FormatProductId(string productId)
    {
        // ...
    }

    // This method accepts a nullable 'formattedProductId'
    // and returns a string or null
    public string? TryGetProductName(string? formattedProductId)
    {
        // ...
    }
}
```

This makes things all fine and clear. We know that the method `FormatProductId` never returns `null` and that it doesn't accept `null` in its parameter. We also know that the method `TryGetProductName` returns a `string` which could be `null` and that the parameter accepts a `string` which could be `null`.

This is great, this means that **we don't have to perform a `null`-check** on `productId`-parameter of the `FormatProductId`-method, right? **Well, not exactly**...

## Confusion: Mixed nullable environments

In an environment where all your code has _Nullable reference types_ enabled, you can trust the output of a method and the input to its parameters. **In a mixed nullable environment, things are not as straight forward** , especially when you look at how IntelliSense in Visual Studio signals **what to expect from the code**.

### Scenario 1: Modern app & legacy library

Imagine that your new modern app has _Nullable reference types_ enabled, but you're using an external library that is legacy and does not have this enabled. This external library can be your own old library or something you've included from NuGet.

The problem now becomes that the external library is signaling, for example, that it has a method that returns a `string` and not a `string?`, so you should be able to trust that it is not `null`, right? **Unfortunately not**. Even with a local non-nullable project, **IntelliSense tells me that the returned `string` is not `null`, even when it is**.

![Value is not null](https://files.sebnilsson.com/web/images/nullable-confusion/value-is-not-null.png)

### Scenario 2: Legacy app & modern library

Imagine that you have just put together a nice library that you want others to use, either in your project, organization or publicly through NuGet. One of the best parts about using _Nullable reference types_ is that **the compiler will warn you if you try to send in a `null` value as a parameter** to a method that explicitly states that it doesn't support `null`.

Nice, now you can clean out all those noisy `null`-checks at the top of all the methods, right? **Unfortunately not**. Your code might be used by another assembly (or an older version of Visual Studio), which **doesn't detect the non-nullability**.

In a way, this means you have to reverse the way you do `null`-checks in your code.

```
public class ProductService
{
    // This method does not accept a null-value
    // and if it does, it should throw an exception
    public string FormatProductId(string productId)
    {
        if (productId == null)
            throw new ArgumentNullException(productId);
        // ...
    }

    // This method accepts null-values
    // and should adjust its logic accordingly
    public string? TryGetProductName(string? formattedProductId)
    {
        return
            formattedProductId != null
            ? GetProductName(formattedProductId)
            : null;
    }
}
```

## Key takeaways

My own take-aways from exploring this aspect of _Nullable reference types_ are:

- When building a library, always check for `null` in incoming method-arguments, even when _Nullable reference types_ is enabled
- When consuming an external legacy library, don't trust the return-type to not be `null` (even if it says it's not)
- In a mixed nullable environment, the feature to guard us against `NullReferenceException`s is likely to mistakenly cause some more of them
- When this feature is fully adopted, there will be a reduction in a lot of the overhead in `null`-handling code

## Thoughts

Hopefully, in .NET 5, this feature is enabled by default and these kinds of confusions, and described associated errors can be avoided.

One idea for an improvement to the IntelliSense-behavior around assemblies that are not known to have _Nullable reference types_ enabled could be to **show all these types as nullable**. Both because it makes things super-clear, but also because of the fact that they actually are nullable.

This change would make **everything in the whole .NET Core CLR light up as nullable**, but as of .NET Core 3.1, it all is nullable, by definition.