Skip to content
Ian Griffiths By Ian Griffiths Technical Fellow I
C# 8.0 nullable references: defeating the point with empty strings

In this post, I'll explain why you should avoid the temptation to apply a quick, and seemingly easy fix for a particular warning that can occur when you start using C#'s nullable references feature. You can silence the compiler by initializing string properties to an empty string, but you really shouldn't.

If you enable nullable references on an existing project, you will often see large numbers of warnings. A particularly common one is CS8618, complaining that a non-nullable property is uninitialized, e.g.:

A C# property of type string, called FavouriteColour, with auto get and set accessors, and a Visual Studio mouse-over message stating that this non-nullable property is uninitialized, asking us to consider declaring the property as nullable.

With a string property, you might be tempted to initialize it with an empty string, e.g.:

public class Person
{
    // This gets rid of the CS8618 warning, but it is
    // A BAD IDEA!
    public string FavouriteColour { get; set; } = "";
}

This will make the warning go away, but it defeats the point of enabling nullable reference types. The whole reason C# 8 introduced its Nullable Reference Types feature was to avoid situations in which properties and other variables do not have a usable value. Before nullable references were available, we had do to one of the following:

  1. Use ad hoc means to attempt to ensure that variables have useful values by the time we use them, with no help from the compiler.
  2. Check for null before attempting to use variables and then decide what on earth to do if we get a null.

With nullable reference types enabled, then we can indicate explicitly the cases in which it is useful to our application to be able to represent the absence of a value, but by default the compiler will give us a great deal of assistance in ensuring that we don't accidentally try to use something that isn't there.

The Introduction to Rx.NET (v6.1) 3rd Edition (2025) Book, by Ian Griffiths & Lee Campbell, is now available to download for FREE.

Initializing a property (or any other variable) with an empty string typically undoes any benefit of enabling nullable reference types, because the empty string effectively becomes null by another name. The fundamental problem remains: we have variables which might sometimes contain a special value that signifies the absence of a real value, we're just using a different special value now. And unlike null, the empty string gets no special analysis from the compiler.

The basic problem that CS8618 is warning us about above remains: it's possible to finish constructing an instance of our Person class without having provided a meaningful value for FavouriteColour. (If having no favourite colour is a position you need your application to support, you can just use string?, because nullability is a reasonable way to indicate that. A better way than using an empty string, in fact.)

If your application absolutely needs a property to be present, the best way to do this is to use the correct-by-construction technique describe in my recent blog on nullable references and serialization in which you define constructors that force code to supply all required values:

public class JsonSerializableType
{
    public JsonSerializableType(
        int favouriteInteger, string favouriteColour)
    {
        FavouriteInteger = favouriteInteger;
        FavouriteColour = favouriteColour;
    }

    public int FavouriteInteger { get; set; }

    public string FavouriteColour { get; set; }
}

If constraints prevent you from using this technique (e.g, some frameworks require types to supply a zero-argument constructor) then that blog describes some alternatives that still enable you to retain the fundamental benefit of nullable reference types.

Programming C# 12 Book, by Ian Griffiths, published by O'Reilly Media, is now available to buy.

It's not really about null

The critical insight here is that none of this is really about null. It's about knowing whether a valid value is available at a particular point in your code. C# has always gone to some lengths to try to ensure this with its definite assignment rules, but it was easy to undermine the compiler's attempts to keep you safe: assigning null to a reference type variable would satisfy the definite assignment rules. These rules were not designed to determine whether the value assigned was actually usable.

In fact the same problem exists for value types. The compiler will require a local variable of type int to be initialized before you attempt to read it, but it won't stop you from picking some arbitrary value just to keep the compiler quiet. If you set an int to 0, the compiler has no way of knowing whether that 0 correctly reflects something about the application's state, or is just a convenient default to use until the real value is available.

Using 0 as a placeholder value in an int variable before you have the real value is conceptually no different from initializing a reference variable to null. Practically it has one difference: at least a null reference will fail conspicuously if you erroneously attempt to use it before it is ready; with an int, if you accidentally end up using something that was meant to be a placeholder value, you probably won't get a runtime error. You'll just get incorrect behaviour.

And that's what's so insidious about initializing a string variable with an empty string as a lazy way to handle the fact that you don't have the right value yet. You've still got the same basic problem you had before, but it will now fail in less conspicuous ways, increasing the chances of incorrect program behaviour going undetected. Better to crash quickly before your program's logic error can cause lasting damage.

If you want to get the benefits that nullable reference types offer, you can't just do the minimum required to keep the compiler happy. (If you want to take that approach, the quickest path is just to turn the feature off again.) To reap the potential value, you need to use techniques such as those shown in my recent nullable references and serialization blog. Let the compiler guide you in reworking your code to reduce the chances of getting unintended nulls. It is by working with, not against the nullable references feature, that you will see the greatest improvements.

FAQs

Why is initializing string properties to empty strings a bad practice when using nullable references? Initializing a string property to an empty string defeats the purpose of nullable reference types. The empty string effectively becomes null by another name, representing the absence of a real value. The fundamental problem remains: variables might contain a special value signifying missing data, but unlike null, the empty string gets no special analysis from the compiler, so errors may go undetected.
What is the CS8618 warning and what does it indicate? CS8618 is a compiler warning stating that a non-nullable property is uninitialized. This warning appears when you have a property of a non-nullable type (like string rather than string?) that is not guaranteed to have a value assigned before the constructor completes. The compiler is alerting you to a potential null reference scenario.
What is the correct-by-construction technique for handling required properties? The correct-by-construction technique involves defining constructors that force calling code to supply all required values. By requiring non-nullable parameters in the constructor and assigning them to properties, you guarantee that the object cannot be created without providing meaningful values, eliminating the possibility of uninitialized properties. In newer versions of C# you can instead apply the 'required' keyword to a property.
Why is the nullable reference feature not really about null specifically? The feature is fundamentally about knowing whether a valid, usable value is available at a particular point in your code. C# has always had definite assignment rules for this purpose, but they only ensure a variable has some value assigned, not that it is a meaningful value. Using null, empty strings, or zero as placeholder values all represent the same conceptual problem: representing missing data with a sentinel value.
Why is an empty string placeholder worse than null in terms of failure behavior? At least a null reference will fail conspicuously with an exception if you erroneously attempt to use it before it is ready. With an empty string or zero as a placeholder, if you accidentally use it before the real value is available, you probably will not get a runtime error. You will just get incorrect behavior, which may go undetected and cause lasting damage.

Ian Griffiths

Technical Fellow I

Ian Griffiths

Ian has worked across an extraordinary breadth of computing - from embedded real-time systems and broadcast television to medical imaging and cloud-scale architectures. As Technical Fellow at endjin, he brings this deep cross-domain experience to bear on the hardest technical problems.

A 17-time Microsoft MVP in Developer Technologies, Ian is the author of O'Reilly's Programming C# 12.0 and one of the foremost authorities on the C# language and high-performance .NET development. He's a maintainer of Reactive Extensions for .NET, Reaqtor, and endjin's 50+ open source projects.

Ian has created Pluralsight courses on WPF fundamentals, WPF advanced topics, WPF v4, and the TPL, and has given over 20 talks at conferences worldwide. Technology brings him joy.