21

Suppose I have the following:

public record Settings 
{
  public int Setting1 { get; init; }
}

public record MoreSettings : Settings
{
  public string Setting2 { get; init; }
}
...

var settings = new Settings { Setting1 = 1 };

// This is an error - CS0117
MoreSettings moreSettings = settings with { Setting2 = "A string setting" };

Is there a clean way to achieve this? Do I have the syntax wrong?
Obviously, in this contrived case I could just manually copy each base property.

var moreSettings = new MoreSettings { Setting1 = settings.Setting1, Setting2 = "A String!" };

But what if the base record type has lots of properties?

John Maillet
  • 680
  • 8
  • 23
  • This is not supported. In the case of a base record with lots of properties, you will have to copy them all, one by one. You might want to hop over to [GitHub](https://github.com/dotnet/csharplang) and make a suggestion, or look for an existing one, and cast your vote for it. – Lasse V. Karlsen Oct 27 '20 at 15:22
  • This isn't possible, and can't be in the general case - what if MoreSettings doesn't define a parameterless constructor for example? – Yair Halberstadt Oct 27 '20 at 15:28
  • 2
    That's what I was afraid of. I've been spending too much time in Typescript land. – John Maillet Oct 28 '20 at 14:17

1 Answers1

39

From your original question,

MoreSettings moreSettings = settings with { Setting2 = "A string setting" };

with keyword internally ensures call to the copy constructor of the type, before changing the specified properties. The Settings record, in the example, does not have a property called Settings2, which is why this is not allowed.

Regarding your second approach, if the base class has a lot properties, you could introduce an additional constructor for the MoreSettings record, which accepts an instance of Settings as workaround for not having to add each of the properties as in the second example given in OP.

Instead, you could,

public record MoreSettings : Settings
{
   public MoreSettings(Settings parent) : base(parent) { }
   public string Setting2 { get; init; }
}

Now you could call make the call as

MoreSettings moreSettings = new MoreSettings(settings) { Setting2 = "A string setting" };

You could also remove the MoreSettings on right hand side by making use of Target-Type New expressions in C# 9

MoreSettings moreSettings = new (settings)  { Setting2 = "A string setting" };
Anu Viswan
  • 17,797
  • 2
  • 22
  • 51