Предположим, мы пишем приложение, в котором необходимо работать с данными пользователя:
У класса User
есть два свойства: ID
– уникальный идентификатор и Name
– имя пользователя. В данном случае идентификатор имеет тип данных int (целочисленный). Однако ID
может быть типом string или GUID. Чтобы это предусмотреть, можно пойти одним из путей:
- Написать много реализаций класса под каждый тип данных.
- Использовать тип Object как универсальный тип данных.
- Использовать обобщённые типы.
Первый вариант не подходит из-за избыточности кода. Разберём второй:
Теперь используем этот класс:
Код отработает без ошибок, но в нем есть подводные камни. При присвоении свойству Id
целочисленного значения происходит процесс упаковки (boxing):
При получении данных обратно в переменную типа int выполняется процесс распаковки (unboxing).
Упаковка и распаковка ведут к снижению производительности, так как требуют затрат ресурсов системы на преобразование типов. Кроме того, такой способ не является безопасным (type safety). Если в переменную (свойство объекта) типа Object упакована строка (string), то при попытке ее распаковки в int мы получим исключение InvalidCastException.
Программа скомпилируется, а исключение выскочит во время её выполнения. Подобных ошибок можно избежать, используя обобщённые типы.
Обобщение типа
Синтаксис:
Перепишем класс с использованием шаблона:
При создании экземпляра класса вместо буквы Т нужно указать тот тип данных, который нам необходим:
Допускается использование несколько универсальных параметров:
Обобщения также могут быть применены к интерфейсам, классам, структурам, методам и делегатам.
Ограничения универсального параметра
Универсальным параметром можно типизировать любой обобщённый тип любым типом данных, однако встречаются ситуации, когда необходимо конкретизировать обобщение. Для этого используется ключевое слово where:
Выражение where T: User говорит о том, что универсальный параметр T должен быть представлен классом User
, или его наследником. В качестве ограничения может применяться только один класс.
Ограничением могут быть следующие типы:
- классы;
- интерфейсы;
- class – универсальный параметр должен быть классом;
- struct – универсальный параметр должен быть структурой;
- new() – универсальный параметр должен иметь общедоступный (public) конструктор без параметров.
При использовании нескольких ограничений, задать их можно в строгом порядке:
- Название класса, class или struct. Эти ограничения нельзя применять одновременно.
- Название интерфейса.
- new().
Можно задать ограничения нескольким универсальным параметрам:
Таким же способом можно задать ограничение обобщённых методов:
Универсальный тип метода DoAction
ограничен классом User
или его наследником.
Обобщения присутствует во многих языках программирования. Шаблонизация используется фактически во всех динамических структурах языка C# (списки, очереди, стеки и т. д). Знание этого инструмента программирование поможет вам создавать более универсальные структуры и алгоритмы. Удачи в обучении!
Комментарии