Ilya Chernomordik
Ilya Chernomordik

Reputation: 30295

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

Upvotes: 17

Views: 8069

Answers (2)

StuartLC
StuartLC

Reputation: 107327

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 ""

Upvotes: 8

Milney
Milney

Reputation: 6427

Initialise them in the definition

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

The object initializer syntax "new Request { }" is just syntactic sugar for new then assignment, so don't think you can get that to error in this case

Upvotes: 5

Related Questions