День 1187. #ЗаметкиНаПолях
Апкаст Записей
Интересную особенность записей обнаружил Фил Хаак. Вы не можете сделать апкаст, как для класса. Допустим, у нас есть следующие записи.
Базовая (она же DTO для обновления):
public record PersonUpdate()
{
public string Name {get; init;}
//... остальные свойства
};
Производная (с добавленным свойством Id):
public record Person(int Id)
: PersonUpdate() {};
Задача стандартная: получить Person из базы или API, создать PersonUpdate с изменёнными свойствами и отправить его в метод UpdatePerson. Код получается довольно простой:
var person = GetPerson(42);
var updated = (PersonUpdate)person
with { Name = "New Name", …};
UpdatePerson(42, updated);
Вроде всё должно работать. Но давайте посмотрим, что у нас получилось в updated:
Console.WriteLine(updated);
// Вывод:
// Person { Name = New Name, Id = 42 }
Свойство обновилось, но тип всё равно производный и содержит Id. Это, конечно, может не быть проблемой, но для Фила стало, т.к. API для обновления принимал объект без свойства Id, а в этом случае выдавал ошибку.
Фил спросил сообщество, как решить эту проблему. Предлагались разные варианты от использования маппера до сериализации/десериализации. Но решение нашлось довольно элегантное.
public record PersonUpdate()
{
//... свойства
public static PersonUpdate
Copy(PersonUpdate updated) => new(updated);
};
Статический метод принимает запись и выдаёт её копию. И действительно:
var updated = PersonUpdate.Copy(person)
with { Name = "New Name"};
Console.WriteLine(updated);
// Вывод:
// PersonUpdate { Name = New Name }
Источник: gist.github.com/haacked…7c09d28b