
C # 9 дает вам лучший способ создания объектов значений используя достаточно простой код. Но даже если вас не интересуют объекты значений, ключевое слово new имеет несколько интересных изменений в новой версии C#. Я не скажу, что сам по себе C # 9 стоит перехода на .NET 5 (хотя я мог бы сделать такое заявление о C # 8 и .NET Core 3.x). Новая версия C # 9 — это больше, чем просто приятная функция .NET 5, и вот мои любимые новые функции.
Неизменяемые объекты
Я большой поклонник дизайна, ориентированного на предметную область. Одной из ключевых концепций этого подхода являются так называемые объекты значений: объекты, которые считаются идентичными, потому что они имеют одинаковые значения в своих свойствах, а не потому, что у них общий первичный ключ или место в памяти. Адрес — хороший пример объекта значения: два адреса одинаковы, если у них одинаковый город / улица / и т.д. даже если они от двух разных клиентов, и один из них является адресом доставки, а другой — адресом для выставления счетов.
Структуры (и другие типы значений) работают таким же образом при сравнении, но со структурами, присвоение значения из одной структуры с другой копирует данные (с классами все по-другому: присвоение одной переменной другой просто копирует указатели вокруг, и обе переменные в конечном итоге указывают на один и тот же объект в памяти вместо получения собственных копий данных).
Проблема с созданием копии объекта-значения заключается в том, что две копии могут иметь отдельные изменения, внесенные в них — это началось с того, что два идентичных объекта разошлись. В результате объекты-значения часто становятся неизменяемыми, что требует значительного количества кода для настройки в .NET. Это фактически делает копирование данных более серьезной проблемой: теперь у вас есть данные, которые гарантированно идентичны, и занимают вдвое больше места, чем необходимо.
Но в C # 9 вы можете просто создать запись и получить объект значения, который делает все, что вы хотите. Вот неизменный объект значения Address:
А вот как я могу создать два объекта Address:
Хотя это выглядит так, как будто я создаю ссылочный тип, когда вы сравниваете два объекта записи, они сравниваются как типы значений — значение имеет значение свойств (при условии, что два объекта относятся к одному типу). Итак, в этом коде тест будет пройден, потому что свойства Street и City имеют одинаковые значения в обоих объектах:
С другой стороны, когда вы назначаете переменные, записи работают как ссылочные типы: указатели перемещаются, но данные не копируются. В этом коде переменные addr1 и addr2 указывают на один и тот же объект в памяти:
Как и классы, записи поддерживают наследование и интерфейсы. Записи также содержат полезный метод ToString. Вызов метода ToString для моего объекта Address приводит, например, к следующему:
Управление и определение свойств в записях.
Как и любое свойство, объявленное как доступное только для чтения (например, мои свойства Street и City), этот код не будет работать, потому что мои свойства, как определено, могут быть установлены только в конструкторе:
В C # 9 теперь можно указать, что свойство readonly может быть установлено при создании экземпляра объекта, пометив свойство как «способное к инициализации», например:
Этот код, который устанавливает свойства при создании экземпляра объекта, теперь будет работать:
Если я хочу, при назначении переменной записи я могу принудительно создать копию с помощью ключевого слова with. Обычно я делаю это, потому что хочу изменить значение некоторого свойства как части копии. Этот код, например, создает новый адрес, но с измененным значением в Городе:
Я также могу выделить свойства моей записи в отдельные переменные. Этот код перемещает мои свойства Street и City в две строковые переменные:
Это называется «деконструкцией» записи, и если вы недовольны тем, как деконструируется ваша запись, вы можете написать свой собственный метод Deconstruct. Метод Deconstruct принимает выходной параметр для каждой отдельной переменной, которая будет возвращена, а затем в теле метода присваивает значения этим параметрам (обычно из свойств в записи, но вы можете делать все, что захотите).
Этот код, например, переопределяет метод Deconstruct по умолчанию и возвращает только свойство City:
Но я слишком много печатаю. При условии, что я готов отказаться от своих конструкторов, я могу определить свой объект значения Address, просто перечислив его свойства при его объявлении, например (обратите внимание на точку с запятой в конце оператора):
Синтаксис выглядит как результат сворачивания объявления класса и списка параметров из конструктора записи в одну строку. Этот синтаксис не мешает вам добавлять методы, конструктор или метод Deconstruct или даже переопределять свойства вашим собственным кодом.
Этот код, например, добавляет к записи тело и делает свойство Street доступным для записи:
Упрощение создания новых
Пока мы говорим о сокращении количества наборов текста, мое другое любимое изменение касается нового ключевого слова.
В моих предыдущих примерах при создании объекта Address я повторял имя записи по обе стороны от знака равенства. Это поддерживает объявление переменной как другого типа, чем класс или запись (например, объявление переменной с использованием интерфейса или базового класса). Но на самом деле в большинстве случаев имена по обе стороны от знака равенства одинаковы:
В этих случаях в C # 9 вы можете опустить имя класса в правой части и просто свернуть оставшуюся часть инструкции влево. Этот код делает именно то, что делал мой предыдущий оператор:
Microsoft называет это «целевой типизацией», и она работает почти везде, где вы хотите. Я могу использовать этот новый синтаксис как при создании типизированной коллекции, так и при добавлении в нее элементов, как демонстрирует этот код:
Как видите, целевая типизация действительно окупается, когда вы загружаете в коллекцию несколько копий одного и того же типа.
Типизация цели также работает, если вы передаете новый объект типизированному параметру метода, как в этом примере:
В C # 9 есть еще о чем поговорить (включая улучшения сопоставления с образцом — это отдельная тема для публикации). Однако это те, которые я буду использовать постоянно.
Может быть, это все-таки причина для миграции.
Перевод статьи Питера Вогеля.