Search code examples
c#initializationc#-9.0non-nullable

how to tell compiler that property MUST be initialized


I have a class like e.g. this:

public class Foo
{
    public string Bar { get; init; }
    public IImmutableSet<int> Baz { get; init; }
}

When I write it that way, I get a compiler warning

Non-nullable property 'Bar' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. csharp(CS8618)

There is much content addressing this (e.g. here), but as far as I can see, it is just about how to get rid of the message by either setting a default value or by switching off compiler warnings using a "fake-default" like null!.

But in my case, a default value does not make sense. Instead, I'd like to tell the compiler that these properties must be set explicitly, so that it complains if any of them was NOT set prior to using the object. Just like this is the case with unassigned local variables:

enter image description here

Is that possible, and if yes, how?

I know I could just define a single constructor with arguments, so that using that constructor is the only way to create a new instance. But since I have lots of classes like the above, I would need to write quite of lot of extra code (i.e. the constructors). It'd also be clearer for readers of my code if there was a kind of "must-be-initialized" flag.


Solution

  • I think get; only properties with constructors is the way to go.

    public class C
    {
        public C(string a)
        {
            A = a;
        }
    
        public string A { get; }
    }
    

    I would need to write quite of lot of extra code (i.e. the constructors).

    Creating constructors nowadays is very easy. It takes:

    • Ctrl+. + 1 mouse click in VS Code:

      enter image description here

    • Ctrl+. + arrow down + enter in VS:

      enter image description here


    What does Microsoft say?

    1. Working with Nullable Reference Types

    In Working with Nullable Reference Types in Non-nullable properties and initialization shows 3 options:

    a. Constructor

    Constructor binding is a useful technique to ensure that your non-nullable properties are initialized:

    b. nullable backing field

    Unfortunately, in some scenarios constructor binding isn't an option; navigation properties, for example, cannot be initialized in this way. (...) One way to deal with these scenarios, is to have a non-nullable property with a nullable backing field: C#

    private Address? _shippingAddress;
    
    public Address ShippingAddress
    {
        set => _shippingAddress = value;
        get => _shippingAddress
               ?? throw new InvalidOperationException("Uninitialized property: " + > nameof(ShippingAddress));
    }
    

    c. Forced initialisation to null!

    As a terser alternative, it is possible to simply initialize the property to null with the help of the null-forgiving operator (!):

    C#

    public Product Product { get; set; } = null!;
    

    An actual null value will never be observed except as a result of a programming bug, e.g. accessing the navigation property without properly loading the related entity beforehand.

    2. Learn techniques to resolve nullable warnings

    In Learn techniques to resolve nullable warnings again there is no example for get; init; properties and the article suggests two options to deal with the warning:

    • Constructor
    • Initialising to null!;

      to indicate that a member is initialized in other code.

    3. Attributes for null-state static analysis interpreted by the C# compiler

    For more complex scenarios it may be worth looking at Attributes for null-state static analysis interpreted by the C# compiler and use some of the available attributes. I don't think this is a path for everyday code though.