C# 9 – Positional or Nominal Creation

C# allows writing code with positional or nominal code style. Using positional code style, constructors can be used. Object initializer belong to the nominal category. So far the nominal category was restricted because it required writable properties. This can change with C# 9.

Change

Positional Creation

Positional creation is the traditional coding style we use since the first version of C#. We use constructors to initialize an object. With inheritance, the constructor initialization can call into the constructor of the base class. Objects are creating passing parameters to the constructor:

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

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

public class Racer : Person
{
    public string RacingTeam { get; }

    public Racer(string firstName, string lastName, string racingTeam)
    : base(firstName, lastName)
    {
        RacingTeam = racingTeam;
    }
}

Person p = new Racer("Charles", "Leclerc", "Ferrari");

While positional creation was possible with C# 1, the code I’ve used in this sample doesn’t compile with C# 1. We didn’t have auto properties at that time and had to write more lines of code for full properties.

Nominal Creation

With nominal creation the code can be reduced by a few code lines. Instead of defining constructors, an object initializer can be used.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Racer : Person
{
    public string RacingTeam { get; set; }
}

Person p = new Racer { FirstName = "Charles", LastName = "Leclerc", RacingTeam = "Ferrari" };

Object initialization comes with important restrictions. The object initializer is just syntax sugar. Behind the scenes, the object initializer sets properties – after the constructor was invoked. It’s not possible to create immutable types that way. The properties need to have a set accessor, otherwise the object initializer cannot be used. That’s why often I need to use the positional creation style, and I’m happy when frameworks support non-default constructors (like EF Core).

C# 9 Nominal Creation

The new C# 9 records feature is based on the nominal features. To create immutable types, this requires changes. The plan with C# 9 is to support init-only properties by defining the init accessor. These properties can be set after the constructor was running – with the initialization of the object using an object initializer:

public class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

public class Racer : Person
{
    public string RacingTeam { get; init; }
}

Person p = new Racer { FirstName = "Charles", LastName = "Leclerc", RacingTeam = "Ferrari" };

init only members can be initialized at the point of object creation but become readonly after object creation has completed.

Of course, sometimes more complex validation is required that cannot be done with single properties, e.g. using a combination of multiple properties. With single properties, the init accessor can have a code block like we know from the traditional property accessors, so simple checks can be done. For checking multiple properties, the new validator with the init code block can be used:

public class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }

    init
    {
        if (FirstName.Length + LastName.Length > 52)
        {
            throw new Exception("...");
        }
    }
}

C# 9 Factories and With Expressions

To create new objects from existing ones, copy constructors and With factory methods can be used. In the code snippet, the Person class defines a copy constructor that returns a new person. The With method is marked as a factory method and invokes the copy constructor. With the Racer class that derives from Person, a copy constructor is defined that in turn invokes the copy constructor of the base class. This class overrides the With method of the base class to return a Racer. Overriding the method you can see that the return type is changed from a Person to a Racer. This is not possible with C# 8, but there’s a plan for C# 9 to allow this.

After creating the first racer, using the same data the second racer is created using the With method. Because this method is a factory method, the object initializer can be used to make some changes.

public class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }

    protected Person(Person that) => (FirstName, LastName) = (that.FirstName, that.LastName);
    [Factory] public virtual Person With() => new Person(this);
}

public class Racer : Person
{
    public string RacingTeam { get; init; }

    protected Racer(Racer that) : base(that) => RacingTeam = that.RacingTeam;
    [Factory] public override Racer With() => new Racer(this);
}

Person p1 = new Racer { FirstName = "Charles", LastName = "Leclerc", RacingTeam = "Ferrari" };
Person p2 = p1.With() { FirstName = "Arthur" };

C# 9 also allows using a with expression instead of a method invocation:

Person p3 = p1 with { FirstName = "Arthur" };

C# 9 Records

Instead of defining a copy constructor and a With method, C# 9 can create an implementation with the new records syntax. Defining the class, record can be added to automatically create code for a copy constructor, a With method, equality comparisons, and more:

public record class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

public record class Racer : Person
{
    public string RacingTeam { get; init; }
}

Person p1 = new Racer { FirstName = "Charles", LastName = "Leclerc", RacingTeam = "Ferrari" };
Person p2 = p1 with { FirstName = "Arthur" };

Take away

Before C# 9, nominal creation had a limited scope – it was not possible to use this syntax for immutable types. C# 9 changes this, and I think this makes nominal creation the preferred syntax style.
I’m thinking on changing the code in the upcoming edition of my Professional C# book for nominal creation, and reduce positional creation to a minimum, just to keep it so existing code can be read as well. What do you think?

If you’ve read this far, consider buying me a coffee which helps me staying up longer and writing more articles.

Buy Me A Coffee

Enjoy learning and programming!

Christian

Links

Init Only Setters – C# Proposal

Records Work-in-Progress

Records as a collection of features

C# Language Design Meeting from April 13th, 2020 for init-only properties and the roadmap for records

C# Language Design Meeting from April 6th, 2020 for init-only members

More information on C# and programming .NET Core applications is in my book Professional C# 7 and .NET Core 2.0, and in my workshops.

Change / Chance Image ID 115654656 © Fotoaccount | Dreamstime.com

20 thoughts on “C# 9 – Positional or Nominal Creation

  1. The remaining questions is do the properties require initialization. Eg, if you add a new required property to a record, do you get a compiler error if it’s not initialized?

    Like

      1. Named arguments would probably not fall into this category as the compiler reorders for the correct positions. With the definition of a method and a constructor, the position is defined.

        Like

  2. The thing is about all this sort of extension / syntactic sugar / call it what you will is that C# is now becoming idiomatic – like a spoken language – which is, on one level, very cool and quite beautiful, but on another level – it‘ s becoming somewhat write-only, in that programmers are having to learn any number of ways to say the same thing, simply to be able to read someone else’s code.

    I’m not sure whether this is entirely a good thing: in English (also a highly idiomatic language) it’s great in novels but not so great in technical manuals: as such, English is a very difficult language in which to write something exact, which you know people will only interpret in one way. It can also be pointed out that novels written by authors with higher and higher levels of education (and, commensurately, larger and more extensive vocabularies), are understood as intended by smaller and smaller percentages of those who read them.

    If C# programs are technical manuals, and not novels, then as it becomes more idiomatic, I can see problems emerging when code is handed out to programmers other than the ones who wrote it.

    This doesn’t mean that I don’t like all of it, or that I won’t use it any chance I get…. but it does limit the audience for what I write, from what is already an extremely limited number in the first place. If the best art is form dictated by function (Ducati), rather than form over function (Harley Davidson), then – yes – it’s becoming possible to create some really great art with C#… but, of course, any truly great art is in some sense abstruse.

    Like

    1. Is this a real problem? JavaScript has a lot more options, e.g. to create an object – and it’s still possible to read it.

      Of course, there are a few ways doing the same with different syntax, e.g. you can assign anonymous methods or lambda expressions to delegates. You can also initialize objects by using a constructor, or a constructor with an object initializer. Is the object initializer a problem? I think all these language constructs fit nice with the syntax of the language.

      I prefer it that the language is enhanced with features that help creating today’s applications.

      Like

      1. “Is this a real problem?”

        Unfortunately, Christian, in the “real” world, I have to manage a team of developers of varying skill levels attending to many legacy applications along with the development and production of new projects.

        I reviewed your article on switch expressions at one team meeting. It was met with quite some hostility and it was generally felt that these changes made the language unreadable and confusing.

        In reality, they don’t want to have to learn new changes every five minutes. They have lives and families they want to go home to at night.

        Your answer might be to go out and hire better people.
        Where from?

        One agency showed me a stat that IT developers opt for career change within 10 years, having reached burnout.
        And, the remuneration generally does not fit the required skill level.

        My job is to get projects completed on time at an agreed cost.
        The client couldn’t care less as to whether it’s written in Cuneiform.

        Welcome to the real world.

        Like

      2. Alan, of course I understand your issue. For me it’s also important to complete projects on time, but I look at other aspects as well.

        Regarding the switch expression, I think it enhances readability and productivity a lot. In just a few lines it can be seen what’s going on, instead of reading code having more than the doubled number of code lines. Of course this only feels more readable if developers are used to Lambda expressions. If WebForms and Windows Forms are still the projects working on, of course all these new features might not be needed including async, tasks…

        I often see project teams where also unit testing is not important – because the project needs to be completed fast. How much maintaining the project costs afterwards often does not matter in the beginning – the money is coming from another place. Of course, for a company it would be best to look at the complete cost of the project including the maintenance cycle, and what can be used in other places as well.

        Recently I helped a company setting up a new developer team. Searching for developers, experience with existing projects was not that much important. On contrary, it was important that the developers have interest in learning new things, adapting to changes. After a few months the team is already very productive creating new applications with Microsoft Azure, .NET Core and WinUI.

        Like

  3. MS disappoints more and more. Looks like after Ballmer leaving, IQ of the company degraded till 70. I doubt MS has proper professionals at all to “improve” C#. All they do are childish “innovations”, absolutely irrelevant to real life and needs. I think I should stop at C# 7.0 and never ever load new versions.

    Like

    1. Vincie, I don’t have the same feelings. The application landscape where C# is used changed a lot since C# was started. I already found several issues turning on nullability with C# 8, had great code simplifications with switch expressions and the using declaration. C# 8 also made it easy to use async streaming with SignalR. I also try to use immutable types with C# as much as possible – there are just too many restrictions where they can be used. With C# 9 this will be a lot easier with the new features coming.

      Like

    2. A few years old, but feel compelled to comment, since..
      We’ve finally found the one guy who thinks Microsoft actually got *worse* after Ballmer left! Haha, is this Steve’s alt account or something?
      Enjoyed this article, btw. Definitely helped me make some mental connections. Thanks!

      Liked by 1 person

  4. Hi, you wrote the below code, I cannot compile it in .NET 6.
    Error IDE1007 The name ‘init’ does not exist in the current context.

    public class Person
    {
    public string FirstName { get; init; }
    public string LastName { get; init; }

    init
    {
    if (FirstName.Length + LastName.Length > 52)
    {
    throw new Exception(“…”);
    }
    }
    }

    Like

    1. Hi Shervan,
      thanks for letting me know about this. I’ve written this article before the release of C# 9, and the init block didn’t make it into the release. I need to write an update on this one.

      If you need additional initialization this way, use a constructor.

      Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.