DEV Community

mohamed Tayel
mohamed Tayel

Posted on

Understanding NotNullWhen in C#

Meta Descripation:
Learn how the NotNullWhen attribute in C# enhances null-state analysis, ensuring safer and cleaner code by eliminating unnecessary null checks. Discover why it works exclusively with Boolean values and how to use it effectively in your projects.

Introduction

C# 8.0 introduced nullable reference types to reduce null reference bugs by leveraging static analysis. However, the compiler cannot infer intent in every scenario. This is where the NotNullWhen attribute, available in .NET Core 3.0 and later, comes into play. In this article, we’ll explore how NotNullWhen works, why it only supports bool, and how it improves null-state analysis.


What is NotNullWhen?

The NotNullWhen attribute is part of the System.Diagnostics.CodeAnalysis namespace. It allows developers to indicate that a parameter will not be null when a method returns a specific Boolean value (true or false).

Supported Versions:

  • C# Version: 8.0 and later
  • .NET Version: .NET Core 3.0, .NET Standard 2.1, and later versions

To use this attribute, nullable reference types must be enabled in your project.


Why Does NotNullWhen Work Only with bool?

The NotNullWhen attribute is designed to work with Boolean values because they represent conditional logic with two possible outcomes: true or false. This aligns perfectly with null-state static analysis, allowing the compiler to:

  1. Infer that the parameter is not null when the method returns true.
  2. Ensure clarity and consistency in null-state analysis.

Other types, such as int or double, are not suitable because they do not inherently represent conditional states. For example, with an int, it would be unclear whether 0 or any other value signifies a non-null condition.


Example Without NotNullWhen

Let’s look at a validation method that does not use NotNullWhen:

public static bool ValidateProvider(ShippingProvider? provider)
{
    if (provider is null)
    {
        return false;
    }
    return true;
}

// Usage
var provider = GetShippingProvider();
if (ValidateProvider(provider))
{
    provider.ShipOrder(); // Warning: provider might be null
}
Enter fullscreen mode Exit fullscreen mode

Problem:

  • The compiler cannot infer that provider is non-null after ValidateProvider returns true.
  • Developers must add redundant null checks or suppress warnings.

Example With NotNullWhen

Using NotNullWhen, you can explicitly communicate your intent to the compiler:

using System.Diagnostics.CodeAnalysis;

public static bool ValidateProvider([NotNullWhen(true)] ShippingProvider? provider)
{
    return provider != null;
}

// Usage
var provider = GetShippingProvider();
if (ValidateProvider(provider))
{
    provider.ShipOrder(); // No warnings!
}
Enter fullscreen mode Exit fullscreen mode

How It Works:

  • The [NotNullWhen(true)] attribute guarantees that provider is not null when the method returns true.
  • The compiler eliminates nullability warnings and unnecessary null checks.

Setting Up NotNullWhen in Your Project

To use NotNullWhen, ensure your project is configured correctly:

  1. Enable Nullable Reference Types:

    • Add #nullable enable at the top of the file:
     #nullable enable
    
  • Or enable nullable reference types project-wide in your .csproj file:

     <PropertyGroup>
         <Nullable>enable</Nullable>
     </PropertyGroup>
    
  1. Target a Supported Framework:

    • .NET Core 3.0 or later
    • .NET Standard 2.1 or later
    • .NET 5 or later
  2. Ensure C# 8.0 or Later:

    • Set the language version in your .csproj file:
     <PropertyGroup>
         <LangVersion>8.0</LangVersion>
     </PropertyGroup>
    

Comparison: Before and After NotNullWhen

Without NotNullWhen With NotNullWhen
Compiler does not infer null safety. Compiler eliminates null warnings.
Additional null checks are required. Code is cleaner and more readable.
Potential runtime null reference errors. Compile-time safety is enhanced.

Real-World Use Case: Null Validation with NotNullWhen

Here’s how you can use NotNullWhen to handle nullable parameters effectively:

using System.Diagnostics.CodeAnalysis;

public static bool IsValidInput([NotNullWhen(true)] string? input)
{
    return !string.IsNullOrWhiteSpace(input);
}

// Usage
string? userInput = Console.ReadLine();
if (IsValidInput(userInput))
{
    Console.WriteLine($"Input: {userInput}");
}
else
{
    Console.WriteLine("Invalid input.");
}
Enter fullscreen mode Exit fullscreen mode

Outcome:

  • The compiler knows userInput is not null inside the if block.
  • Null reference warnings are eliminated.

Best Practices for Using NotNullWhen

  1. Use for Boolean Logic:

    • Apply the attribute to nullable parameters in methods that return bool.
  2. Avoid Misusing Nullable Types:

    • Use nullable types only when null is a valid state.
  3. Enable Nullable Reference Types:

    • Leverage nullable reference types project-wide for comprehensive null-state analysis.
  4. Document Your Intent:

    • Annotate methods with NotNullWhen to clearly communicate null-state guarantees.

Conclusion

The NotNullWhen attribute is a powerful tool for null-state static analysis in C#. By restricting it to Boolean values, the language ensures clarity, precision, and consistency. Using NotNullWhen, you can write safer, cleaner code and eliminate unnecessary null checks.

If you haven’t yet enabled nullable reference types and explored NotNullWhen, now is the time to start! Try it in your projects to see the difference it makes in reducing null reference bugs.

Top comments (0)