Закрытые Иерархии Классов. Окончание
Начало
Закрытые классы и объединения
Одним из преимуществ поддержки объединений в .NET 11 является то, что вы получаете аналогичную проверку исчерпываемости в выражениях
switch:// 3 разных типа
public record Windows(string Version);
public record Linux(string Distro, string Version);
public record MacOS(string Name, int Version);
// Объединение представляет один из них
public union SupportedOS(Windows, Linux, MacOS);
// Объединение в switch
string GetDescription(SupportedOS os)
=> os switch
{
Windows windows => $"Windows {windows.Version}",
Linux linux => $"{linux.Distro} {linux.Version}",
MacOS macOS => $"MacOS {macOS.Name} ({macOS.Version})",
};
Как и в случае с закрытой иерархией классов, объединение поддерживает проверку исчерпываемости, поэтому вам не нужно обрабатывать варианты «catch-all». Однако в объединении нет иерархии; оно определяет конкретные возможные значения, и компилятор гарантирует обработку всех этих случаев в выражении switch.
Ограничения и особенности
Когда вы применяете ключевое слово
closed к классу или записи, это неявно делает класс абстрактным, поэтому вы не можете использовать модификаторы sealed или static для этого типа. Кроме того, вы также не можете явно использовать abstract для типа, даже если он является абстрактным по логике.При наследовании от закрытого типа производные классы не являются автоматически закрытыми, но их можно объявить закрытыми:
public closed record Animal { }
public closed record Dog : Animal{ }
public record Labrador : Dog { }Можно использовать обобщённые закрытые типы, но если производный класс также является обобщённым, то все его параметры типа должны использоваться в спецификации базового класса:
public closed class Animal<T> { }
class Dog<U> : Animal<U> { } // ОК
class Cat<W> : Animal<int> { } // ошибка CS9383
// Тип 'W' должен быть указан в базовом типе 'Animal<int>', т.к. базовый тип закрыт.Ещё одно тонкое ограничение возникает, если вы применяете ключевое слово
sealed к производным типам. Тогда говорят, что у типа запечатанная иерархия. В этих случаях компилятор может делать ещё более строгие предположения о вашем коде и, следовательно, может добавлять ошибки для сценариев, когда в вашем коде, вероятно, что-то не так. Например, объявим производные типы как sealed и добавим интерфейс IPet:public closed class Animal { }
public sealed class Dog : Animal { }
public sealed class Cat : Animal { }
public interface IPet { }Теперь, если у нас есть код, который пытается явно привести тип из экземпляра Animal к типу IPet, компилятор может «видеть» все производные типы и что ни один из них не реализует IPet, и, следовательно, может на этапе компиляции предположить, что это преобразование завершится неудачей:
Animal animal = GetSomeAnimal();
var pet = (IPet)animal; // CS0030: Cannot convert type 'Animal' to 'IPet' (Нельзя привести тип 'Animal' к 'IPet')
Это превращает гарантированную ошибку времени выполнения в ошибку компиляции. Отлично!
Источник: https://andrewlock.net/exploring-the-dotnet-11-preview-4-closed-class-hierarchies/