Annotating Nulls in C#
Tags: dotnet, tools, csharp, codequality
Published: 2019-09-12 06:16:10

In my prior post I talked about [using functional programming null handling features](https://dev.to/integerman/eliminating-nulls-in-c-with-functional-programming-iaa). While this is a valid approach, it is also one that requires a lot of code changes to achieve. If you don't want to make that drastic of a level of changes, there are a few other options to consider.

One of my favorites since I discovered [ReSharper](https://www.jetbrains.com/resharper/) is a freely-available NuGet package called [JetBrains.Annotations](https://www.nuget.org/packages/JetBrains.Annotations).

This library contains a few attributes that you can use to annotate your classes, methods, properties, fields, and parameters. While these are intended to be analyzed by the ReSharper Visual Studio add-in or the up-and-coming Rider IDE, they have value in their own right even if you don't use these tools.

# NotNull / CanBeNull

Ordinarily you might have a routine that looks something like this:

```cs
public AnalysisResult Analyze(ResumeInfo resume, IContainer container)
{
    if (resume == null) throw new ArgumentNullException(nameof(resume));
   
    IKeywordBonusProvider bonusProvider;
    if (container != null) {
       bonusProvider = container.Resolve<IKeywordBonusProvider>();
    } else {
       bonusProvider = new EntityFrameworkKeywordProvider();
    }

    return CalculateScore(resume, bonusProvider);
}

```

Looking at this, you'd have to scan the method to determine whether it's okay to pass in a null `resume` or `container` either when calling the method or when adding new code to a method.

JetBrains.Annotations has a solution for this via the `NotNull` and `CanBeNull` attributes.

```cs
[NotNull]
public AnalysisResult Analyze([NotNull] ResumeInfo resume, 
                              [CanBeNull] IContainer container)
{
    if (resume == null) throw new ArgumentNullException(nameof(resume));
   
    IKeywordBonusProvider bonusProvider;
    if (container != null) {
       bonusProvider = container.Resolve<IKeywordBonusProvider>();
    } else {
       bonusProvider = new EntityFrameworkKeywordProvider();
    }

    return CalculateScore(resume, bonusProvider);
}
```

Here we added attributes to the two parameters indicating what we expect as far as whether they can or cannot be null. Additionally, we annotated the `Analyze` method's return type with `NotNull` to indicate that it does not ever return a null value.

These attributes are not enforced by the compiler or runtime, but they are interpreted via Rider and ReSharper.

![ReSharper displaying a nullability warning](https://thepracticaldev.s3.amazonaws.com/i/481twgrc5uujnn74ham4.png)

Above, ReSharper is warning about potentially passing a potentially null value into a method as a parameter decorated as `NotNull`.

# ItemNotNull / ItemCanBeNull

Similarly to `NotNull` and `CanBeNull`, you can annotate collections with `ItemNotNull` and `ItemCanBeNull` to indicate whether or not null values are possible inside of a collection.

```cs
[NotNull, ItemCanBeNull]
public IEnumerable<string> GetTestingStrings()
{
    return new List<string> {"NUnit", "XUnit", null, "MSTest V2"};
}
```

Here we note that the collection itself is not null via `NotNull`, but the collection can have null entries as annotated by `ItemCanBeNull`.

# Pure

Pure methods are methods that do not have side effects outside of the method itself or on the objects passed into the method. As such, pure methods can be called infinitely with the same parameters and get the same output without any modifications being made to other objects or systems.

An extremely simple pure method is below:

```cs
[Pure]
public static int AddNumbers(int x, int y) => x + y;
```

The `Pure` attribute here is another piece of syntax to help ReSharper and Rider offer warnings if the return value is not being used (meaning the method invoke is meaningless), but it can also be used as documentation for your team to indicate that the method should never be modified to have any observable side effects.

# PublicAPI

Public API is another simple one. It essentially marks a method as intended to be consumed by something outside of the project and that the class or method should not be considered unused / dead code. 

If you were building a class library, for example, you would often have classes or methods not invoked by your code (though hopefully still called by test code) that are used by external applications.

You annotate those members via the `PublicAPI` attribute like this:

```cs
[PublicAPI]
public void NothingLooksLikeItUsesThis() {
   // Do something important
}
```

This attribute can also be helpful in restraining yourself when refactoring / restructuring an application. If you were tempted to change the signature of a public method that is invoked by something outside of your project, you might not otherwise realize it unless it had a `PublicAPI` attribute assigned to it - leading to potential crashes or development headaches later on.

# Other Attributes

There are a number of other attributes that JetBrains uses for display and advanced code completion, but these do not necessarily add much value without those tools. See the [online documentation](https://blog.jetbrains.com/dotnet/2018/05/04/better-null-checks-string-formatting-path-completion-jetbrains-annotations/) for a larger list of available attributes.

---

While not for everyone and not explicitly checked by the compiler, Jetbrains.Annotations are an interesting hybrid of inline null-safety documentation and advanced IDE support if you happen to use JetBrains products. If this is something that might help you out, I recommend giving it a try as a low-risk way to be explicit about null values.

If you're looking for something with more compile-time enforcement, take a look at [my article on functional programming techniques to eliminating nulls in C# code](https://dev.to/integerman/eliminating-nulls-in-c-with-functional-programming-iaa).

{% link https://dev.to/integerman/eliminating-nulls-in-c-with-functional-programming-iaa %}