Primary constructors

C# 9.0 now introduces a new simplified way to construct your classes. Most constructors are very redundant as the arguments of the constructor map one to one with a property of the same name. There's now a way to get rid of all that extra code.

Before we had something like this

public class Person
{
    public Person(string firstName, string lastName)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
    }

    public string FirstName { get; }
    public string LastName { get; }
}

Eleven lines of code just to create a simple POCO. Now you can do the same thing in one line.

public record Person(string firstName, string lastName);

primary-constructor

At the time of this writing, primary constructors only seem to work with records. Classes and structs are not supported yet but should be implemented in the final release.

Object initializer

Did you notice the subtle difference? Primary constructors also use the new init properties when generating the constructors and their properties. It means that we can theoretically set a property at the time of construction with object initializer. Let's try it.

[Fact]
public void PrimaryConstructorWithAutoConstructor()
{
    var p = new Person("Miguel", "Bernard") { lastName = "test" };

    Assert.Equal("Miguel", p.firstName);
    Assert.Equal("test", p.lastName);
}

Work just as expected. At object initialization, "Bernard" gets replaced by "test". However, you cannot use object initialization for the required properties of your primary constructor.

[Fact]
public void PrimaryConstructorWithAutoConstructorInstead()
{
    // This is not valid
    var p = new Person("Miguel") { lastName = "test" };

    Assert.Equal("Miguel", p.firstName);
    Assert.Equal("test", p.lastName);
}

primary-constructor-invalid-object-initialization

Default values

Now I wonder if I can push the primary constructor a bit further and set default values for some parameters.

public record PersonWithDefaultValue(string firstName, string lastName = "lastName");

[Fact]
public void PrimaryConstructorWithDefaultValue()
{
    var p = new PersonWithDefaultValue("Miguel");
    Assert.Equal("Miguel", p.firstName);
    Assert.Equal("lastName", p.lastName);
}

Again, it works just as you would expect. I see great potential in this new feature, especially for POCOs/DTOs and simple objects. Removing all that useless boilerplate code will reduce the friction of creating new types, help to get rid of the primitive obsession and reduce the complexity of your codebase.

References