Меняем Поведение Конструкторов Копирования Записей
Когда вы используете записи, вы можете создать новый экземпляр, используя ключевое слово new или скопировать экземпляр с некоторыми изменениями, используя выражение with. Выражение with копирует все поля из исходного экземпляра, а затем применяет изменения.
var john = new Person("John", "Doe");
var Jane = john with { FirstName = "Jane" };
record Person(string FirstName, string LastName);Если запись содержит изменяемый объект, например List<T>, экземпляр будет совместно использоваться оригиналом и копией. Это связано с тем, что выражение with не клонирует изменяемый объект:
var john = new Person("John", "Doe") {
Phones = ["1234567890"]
};
var jane = john with { FirstName = "Jane" };
jane.Phones.Add("123");
// Both list are modified
Console.WriteLine(john.Phones.Count); // 2
Console.WriteLine(jane.Phones.Count); // 2
public record Person(string FirstName, string LastName)
{
public List<string>? Phones { get; init; }
}При использовании выражения with вызывается созданный компилятором метод <Clone>$. Он вызывает конструктор копирования. Если вы не реализуете конструктор копирования, компилятор сгенерирует конструктор, копирующий все поля. Вот сгенерированный код для записи Person:
internal class Person
{
// ...
[CompilerGenerated]
public virtual Person <Clone>$()
{
return new Person(this);
}
[CompilerGenerated]
protected Person(Person original)
{
<FirstName>k__BackingField = original.<FirstName>k__BackingField;
<LastName>k__BackingField = original.<LastName>k__BackingField;
<Phones>k__BackingField = original.<Phones>k__BackingField;
}
}
Если вы хотите клонировать свойство Phones при использовании выражения with, вы можете реализовать конструктор копирования вручную:
public record Person(string FirstName, string LastName)
{
public List<string>? Phones { get; init; }
protected Person(Person other)
{
FirstName = other.FirstName;
LastName = other.LastName;
if(other.Phones != null)
Phones = new List<string>(other.Phones);
}
}
var john = new Person("John", "Doe") {
Phones = ["1234567890"];
var jane = john with { FirstName = "Jane" };
jane.Phones.Add("0987654321");
// Изменится только оригинал, но не копия
Console.WriteLine(john.Phones.Count); // 1
Console.WriteLine(jane.Phones.Count); // 2
Источник: https://www.meziantou.net/customizing-the-behavior-of-the-record-copy-constructor.htm