C# > Language Features by Version > C# 6 to C# 12 Highlights > Nullable Reference Types (C# 8)

Nullable Reference Types Introduction

This snippet demonstrates the use of nullable reference types, introduced in C# 8, to improve null-safety in your code. It highlights how to enable and utilize nullable reference types to reduce the risk of NullReferenceExceptions.

Enabling Nullable Reference Types

Nullable reference types are disabled by default. To enable them, you need to add the <Nullable>enable</Nullable> element within a <PropertyGroup> in your project's .csproj file. This instructs the compiler to treat reference types as non-nullable by default.

<!-- Add this to your .csproj file -->
<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

Declaring Nullable Reference Types

A non-nullable reference type (string name) is guaranteed to never be null. The compiler will issue warnings if you try to assign null directly or pass a potentially null value to a non-nullable reference. A nullable reference type (string? nullableName) can hold either a string value or null. The ? symbol indicates that the variable is nullable.

string name = "John Doe"; // Non-nullable string
string? nullableName = null; // Nullable string

Checking for Null

Before accessing members of a nullable reference type, you must check if it's null. The compiler will warn you if you access a nullable reference type without a null check. Using an if statement ensures that you only access the Length property if nullableName is not null.

if (nullableName != null)
{
    Console.WriteLine("Name: " + nullableName.Length);
}

Suppressing Nullable Warnings

The null-forgiving operator (!) can be used to suppress nullable warnings. Use this sparingly and only when you are absolutely certain that the variable is not null at that point, even though the compiler can't prove it. Overuse of this operator defeats the purpose of nullable reference types. In this example, even if GetNameFromSomewhere() can return null, we are telling the compiler that at this specific point maybeNull is not null, so the compiler will not throw a warning.

string? maybeNull = GetNameFromSomewhere();
string definitelyNotNull = maybeNull!;
Console.WriteLine(definitelyNotNull.Length);

Concepts Behind Nullable Reference Types

The core concept is to differentiate between variables that are designed to be nullable and those that are not. This allows the compiler to help you catch potential NullReferenceExceptions at compile time, rather than runtime. Nullable reference types are a form of static analysis, providing improved code safety. It's important to understand that these are design-time checks; they don't prevent null assignments at runtime if you explicitly bypass the compiler's checks (e.g., using the null-forgiving operator excessively).

Real-Life Use Case

Consider reading configuration data from a file. Some configuration values might be optional, while others are required. Using nullable reference types allows you to clearly indicate which values can be null and forces you to handle potential null values gracefully. Without nullable reference types, you'd have to rely on documentation or conventions, which are prone to errors.

Best Practices

  • Enable nullable reference types for your entire project.
  • Treat compiler warnings as errors initially to identify potential null-related issues.
  • Use the null-forgiving operator sparingly and only when you are absolutely certain that a value is not null.
  • Properly handle null values using null checks or other appropriate techniques (e.g., null-conditional operator).
  • Consider using the ?? (null-coalescing) operator to provide default values when a nullable value is null.

Interview Tip

Be prepared to explain the purpose of nullable reference types and how they improve code safety. Understand the difference between string and string?. Know how to enable nullable reference types in a project and how to handle nullable warnings.

When to Use Them

Use nullable reference types in any new C# project and gradually enable them in existing projects. They are especially valuable in large codebases where the risk of NullReferenceExceptions is higher. They help improve the overall quality and maintainability of your code.

Alternatives

Before C# 8, developers relied on coding conventions and code analysis tools to detect potential null-related issues. These approaches were less effective and required more manual effort. Another alternative is defensive programming, which involves adding null checks everywhere, but this can lead to verbose and less readable code.

Pros

  • Improved code safety by reducing the risk of NullReferenceExceptions.
  • Compile-time warnings help catch potential null-related issues early.
  • Clearer code by explicitly indicating which variables can be null.
  • Better maintainability by making nullability more explicit.

Cons

  • Requires changes to existing code to address nullable warnings.
  • Overuse of the null-forgiving operator can defeat the purpose.
  • Can add some overhead to the development process initially.

FAQ

  • What happens if I don't enable nullable reference types?

    If you don't enable nullable reference types, the compiler will treat all reference types as potentially nullable, but it won't issue warnings if you don't perform null checks. You won't benefit from the improved null-safety features.
  • Are nullable reference types a runtime feature?

    No, nullable reference types are primarily a compile-time feature. They provide static analysis to help you catch potential null-related issues before runtime. The runtime behavior of reference types remains unchanged.
  • Can I disable nullable reference types for specific files?

    Yes, you can use pragma directives (#nullable disable and #nullable restore) to disable nullable reference types for specific sections of code.