TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.76K subscribers
Post #2133 2.11K
День 1760. #ЗаметкиНаПолях
Меняем Поведение Конструкторов Копирования Записей
Когда вы используете записи, вы можете создать новый экземпляр, используя ключевое слово 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
  • 👍 23
  • 👎 2
More from @netdeveloperdiary
  1. Oct 9, 2026День 2809. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Окончание Начало 3.…
  2. Oct 8, 2026День 2808. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Начало В ИТ есть се…
  3. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
  4. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  5. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
  6. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →