Why does C# 9.0 have a "with" keyword instead of using existing syntax?

923 views Asked by At

The C# 9.0 with keyword facilitates the creation of new records from existing records.

My question is: what technical reasons did or could motivate introducing this specific feature with a new keyword and syntax instead of just generating a new function with named parameters.

For example consider the example from the docs:

    Person person1 = new("Nancy", "Davolio") { PhoneNumbers = new string[1] };
    Person person2 = person1 with { FirstName = "John" };

Instead we could have had something like:

    var person1 = new Person("Nancy", "Davolio", new string[1]);
    var person2 = person1.With(FirstName:"John");

I assume that adding new syntax to a widely used language like C# based on careful consideration and that possibly it is documented somewhere.

2

There are 2 answers

0
Ben Voigt On

It was originally proposed with a With method.

enter image description here

In order to default parameter values from another object instance, there was a complicated new language feature proposed: caller-receiver parameters

The new design preserves the convenience of the old design but is much simpler for the compiler to implement.

1
Olivier Jacot-Descombes On

How would you implement this With method? Let's make a try:

public Person With(string firstName = null, string lastName = null,
                   string[] phoneNumbers = null)
{
    return new (
        firstName ?? this.FirstName,
        lastName ?? this.LastName,
        phoneNumbers ?? this.PhoneNumbers
    );
}

The problem with this approach is that we are not able to set a field to null as in

Person person2 = person1 with { phoneNumbers = null };

Therefore another approach is required. We could add overloads of every possible parameter combination

public Person With(string firstName);
public Person With(string lastName);
public Person With(string[] phoneNumbers);
public Person With(string firstName, string lastName);
public Person With(string firstName, string[] phoneNumbers);
public Person With(string lastName, string[] phoneNumbers);
public Person With(string firstName, string lastName, string[] phoneNumbers);

The problem is that the number of required overloads grows exponentially with the number of parameters:

2n - 1.


Yet another approach would be to let the compiler handle a call like person1.With(FirstName:"John"). It would be syntactic sugar for compiler generated code. This introduces other problems:

  • It does not behave as we would expect from other methods.
  • How would Intellisense display the parameters of this pseudo method? As in our first attempt? This would be misleading because of this nulling problem.
  • It could conflict with a user defined method of the same name.

Some interesting links relating to the implementation of Non-destructive mutation (with keyword):