Union-типы: отказ от object и наследования
Самое значимое изменение в C# 15 — введение union-типов. Это структура, позволяющая объединять несколько типов без использования наследования или интерфейсов. Компилятор знает полный список типов внутри union, что делает код безопаснее и избавляет от необходимости обрабатывать default в switch-выражениях.
Ранее для обработки разнородных ответов API приходилось создавать абстрактные базовые классы или использовать object с последующим приведением типов, что часто приводило к ошибкам runtime. Union-типы решают эту проблему на этапе компиляции.
Сравнение подходов: до и после
Ниже приведено сравнение реализации обработки результатов API в старом и новом стиле:
| Характеристика | Старый подход (Наследование) | Новый подход (Union-типы) |
|---|---|---|
| Объявление типа | Абстрактный класс ApiResult и наследники |
Union: public union ApiResult(Success, ValidationError) |
| Безопасность | Риск пропуска новых наследников в default |
Компилятор требует обработки всех типов |
| Сложность кода | Высокая (иерархия классов) | Низкая (плоская структура) |
| Методы внутри | Требуют виртуальных вызовов | Можно определить методы прямо в union |
Закрытые иерархии (Closed Hierarchies)
Параллельно с union-типами вводится модификатор closed. Он запрещает наследование от класса за пределами сборки, где он объявлен. Это гибрид internal и sealed, полезный для библиотек, где нужно ограничить расширение функционала внешними разработчиками.
- Классы с
closedвсегда абстрактны. - Позволяет использовать exhaustive switch без ветки default.
- Применимо только к классам и record class (не к структурам и интерфейсам).
Именованные break и continue
Мелкое, но важное улучшение синтаксиса. Теперь можно прерывать или пропускать итерации внешних циклов, помечая их метками:
outer: foreach (var item in workList) {
switch (item.Type) {
case WorkItemType.StopAll:
break outer; // Выход из внешнего цикла
}
}
Это устраняет необходимость во временных флагах (например, bool stopProcessing) и делает код чище. Поддерживаются конструкции: for, foreach, while, do, switch.
Индексаторы расширений
Появилась возможность определять индексаторы расширения для коллекций. Это позволяет использовать синтаксис collection[index] для типов, реализующих IReadOnlyList или Range, без необходимости писать вспомогательные методы вроде Slice.
Источник: Habr ↗
