Search code examples
.net-5c#-9.0

C#9 + .NET5, nullable enabled: is it possible to use the object initialiser with non nullable properties without using null!?


I'm testing c# 9.0 with NET 5.0 and there is something I don't understand. I enabled <nullable>enable</nullable>.

If I write a class

public class Potato {
    public string Colour { get; set;  }

    public string Flavour { get; set; }
}

I get the warning CS8618: Non-nullable propery 'Colour' must contain a non-null value when existing constructor. Consider declaring the propery as nullable. I do NOT want to declare it nullable, that's why I enabled the tag...

Then, I declare the constructor:

public class Potato
{
    public Potato(string colour, string flavour)
    {
        this.Colour = colour;
        this.Flavour = flavour;
    }

    public string Colour { get; set; }

    public string Flavour { get; set; }
}

So far so good. Now I want to create a Potato object using the object initialiser:

var sweetPotato = new Potato { 
    Colour = "orange",
    Flavour = "tasty",
};

And now I get the complain CS7036: There is no argument given that corresponds to the required formal parameter 'colour' of 'Potato.Potato(string, object). So, it tries to use the constructor I explicitly declared but I didn't introduce the parameters. I should do:

var sweetPotato = new Potato(
    colour: "orange",
    flavour: "tasty");

but here I am not using the object initialiser.

Another option would be declaring the class as

public class Potato
{
    public string Colour { get; set; } = null!;

    public string Flavour { get; set; } = null!;
}

with the null! elements, but this allows some weird things like:

var sweetPotato = new Potato()
{
    Colour = "orange",
};

which I think it is extremely weird. I declared 2 non nullable properties and thanks to the null! I can load the object with one property but not loading the second one. That breaks my nullable tag!

Then my question is: is it possible to use the object initialiser with non nullable properties if the nullable tag is enabled?

If it is not, I think that for classes with non nullable properties, without any null! involved, the default constructor should not be the empty one but the one with all arguments, and it should have to be possible to init an object with the object initialiser form, as long as all non-nullable properties are initialised.


Solution

  • I know this is an old Question but here's the comprehensive answer:

    1. by adding the <nullable>true<nullable> tag you make it so that all nullables must be identified with ?
    2. because of 1., and since you didn't add ? to their types, neither Colour nor Flavour can be NULL
    3. because of 2., you must supply a constructor that initializes values for both Colour and Flavour - the default parameterless constructor won't work as it does nothing by itself and would return an object with NULL in both Colour and Flavour, as that's the default for their types, which you requested the compiler not to allow

    The snippet below:

    var sweetPotato = new Potato { 
        Colour = "orange",
        Flavour = "tasty",
    };
    

    uses the default parameterless constructor and so cannot be used. It is the shorthand equivalent to:

    var sweetPotato = new Potato(); // sweetPotato has Colour and Flavour NULL
    sweetPotato.Colour = "orange";  // sweetPotato still has Flavour NULL
    sweetPotato.Flavour = "tasty";
    

    which as you can see would create the object first (with default values) and assign values later. But by your not allowing the default value of NULL, it fails

    And this is why if you won't allow them to be nullable, you must add a constructor that initializes them, and you must use that constructor

    This wouldn't apply if their types were of a primitive that has a different default - like an int or a double. In that situation, the default parameterless constructor would work. But by using types with a default value of NULL, (like string or any object derived type), you can either make them nullable by adding ? or you must use a constructor that initializes them to something other than NULL.

    Late, Late Edit You can use the object initializer still if you introduce a parameterless constructor that defaults those two properties:

    public Potato()
    {
         Colour = "blue";
         Flavor = "bitter";
    }
    

    Then you can do:

    var bitterPotato = new Potato(); //blue and bitter potato
    var sweetPotato = new Potato
    {
        Color = "orange",
        Flavour = "tasty"
    }; // orange and tasty potato