Search code examples
c#nullable-reference-typesnon-nullable

Is constructor the only way to initialize non-nullable properties in a class in C#?


I have switched to enable nullable in my project that uses C#8. Now I have the following class:

public class Request
{
    public string Type { get; set; }
    public string Username { get; set; }
    public string Key { get; set; }
}

Compiler of course complains that it cannot guarantee that these properties won't be null. I can't see any other way of ensuring this than adding a constructor that accepts non-nullable strings.

This seems fine for a small class, but if I have 20 properties, is this the only way to list them all in a constructor? Is it possible somehow to e.g. enforce it with the initializer:

var request = new Request { Type = "Not null", Username = "Not null" }; // Get an error here that Key is null

P.S. There is a good answer that suggests using iniatlizer for properties, but that does not always work e.g. for types like e.g. Type that one cannot just intialize to some random value


Solution

  • Object initializer syntax is really just short-hand for explicitly assigning to fields or property setters, i.e.

    var request = new Request { Type = "Not null", Username = "Not null" };
    

    Is equivalent to:

    var request = new Request();   // <-- All properties are default
    request.Type = "Not null";     // <-- Username and key are default
    request.Username = "Not null"; // <-- Key is still default
    

    As you can see, the Request instance still goes through several states where the properties are in a default status, which will be null for reference types like string unless you assign a different default value in the constructor, as per the other answers.

    Also, by specifying different default values

    public string Type { get; set; } = ""
    public string Username { get; set; } = "";
    public string Key { get; set; } = "";
    

    is equivalent to assigning these values in a default constructor, i.e.

    public Request()
    {
        Type = "";
        UserName = "";
        Key = "";
    }
    

    As you can imagine, this is a bit wasteful if you then immediately change the value again using object initializer syntax.

    As an alternative, and if you don't require a mutable class, I would suggest instead that you provide one or more constructor overloads, which then supply suitable non-null default values for any missing fields, and then make the properties immutable e.g.

    public class Request
    {
        public Request(string type = "", string userName = "", string key = "")
        {
             Type = type;
             Username = userName;
             Key = key;
        }
    
        public string Type { get; }     // <-- No setter = immutable.
        public string Username { get; }
        public string Key { get; }
    }
    

    You can now instantiate the class without ever going through a state where any of the properties are ever null, and without the overhead of the intermediate default assignment, e.g.

    var myRequest = new Request(key: "SomeKey"); // <-- Username and Type are defaulted to ""