Шаг 251.
Язык программирования C#. Начала.
Обобщенные типы. Передача типа данных в виде параметра

    На этом шаге мы рассмотрим необходимость введения такой возможности.

    Идея использования обобщенных типов базируется на том, что при работе с методами, классами, структурами, интерфейсами или делегатами тип фактически используемых данных передается с помощью специального параметра.


Те "конструкции", которые описываются, начиная с этого шага, в англоязычной литературе описываются термином generics, что можно было бы перевести как обобщения (часто именно так и переводят). Иногда применяют термин "шаблоны" или "универсальные шаблоны". Но эта терминология, скорее, из языка C++. Мы будем использовать термин "обобщенные типы".

    Чтобы лучше понять, зачем все это нужно и как реализуется на практике, представим следующую ситуацию. Допустим, мы хотим описать статический метод, который получает два аргумента одного и того же типа, и при выполнении метода эти аргументы обмениваются значениями. Что примечательно в таком методе? Примечательно в нем то, что алгоритм, который необходимо реализовать в методе, практически не зависит от типа аргументов, передаваемых методу. Самое важное и фактически единственное условие состоит в том, что оба аргумента должны относиться к одному и тому же типу. А какой именно это тип - вопрос второстепенный. Конечно, мы могли бы описать несколько версий метода для аргументов разного типа. Но, во-первых, это очень неудобно и нерационально (поскольку придется фактически переписывать несколько раз один и тот же код - с минимальными поправками на конкретный тип аргументов). Во-вторых, такой подход не всегда приемлем, поскольку типов данных (с учетом различных библиотечных и пользовательских классов) очень много и перебрать их все нет физической возможности. Так какой же выход из ситуации? А выход простой и элегантный. Мы описываем алгоритм выполнения метода, но при этом не конкретизируем тип аргументов, а лишь указываем, что они относятся к одному типу. При вызове метода данный алгоритм выполняется с учетом того, к какому типу относятся аргументы, переданные методу. В этом состоит основная идея, связанная с использованием обобщенных методов.

    Примерно такой же подход используется при работе с обобщенными классами. Скажем, мы хотим описать класс, у которого имеется одно поле некоторого типа. Еще у класса есть конструктор с одним аргументом (того же типа, что и тип поля). Аргумент конструктора определяет значение поля. В классе можно описать метод (без аргументов), отображающий значение поля, а также метод с одним аргументом, позволяющий присвоить полю значение. В общем и целом - стандартная ситуация. Особенность ее лишь в том, что способ описания такого класса практически не зависит от того, к какому типу относится поле класса.

    Важно лишь, чтобы тип поля, тип аргумента конструктора и тип аргумента метода совпадали. Ну, еще тип должен быть таким, чтобы значение этого типа можно было отобразить в консольном окне (то есть чтобы такая операция имела смысл). Вот, собственно, и все. А теперь давайте представим, что нам необходимо несколько классов, отличающихся лишь типом поля. В рамках традиционного подхода для каждого типа данных пришлось бы описывать отдельный класс. Причем если в случае с методом можно было бы воспользоваться перегрузкой (и разные версии метода имели бы одно и то же имя), то при описании нескольких классов каждому классу пришлось бы давать собственное имя. При этом содержимое каждого такого класса было бы во многом одинаково.


Теоретически можно было бы описать один класс с полем типа (класса) Object. Но и в таком подходе есть свои серьезные недостатки, так что это тоже не является решением проблемы.

    Вместо создания нескольких однотипных классов мы можем описать один обобщенный класс. От обычного класса обобщенный отличается тем, что в нем не указан конкретный тип поля. Просто указывается, что поле есть и оно какого-то типа. И аргумент конструктора и метода - того же типа. А какой именно это тип, станет понятно, когда будет создаваться объект. Преимущество данного подхода как минимум в том, что нет необходимости многократно дублировать сходные блоки программного кода.


Обычно интерес представляет объект класса (в данном случае имеется в виду обычный, не обобщенный класс). Объект, как мы знаем, создается на основе класса. В этом смысле класс представляет собой некую абстракцию, которая служит "каркасом" для создания объекта. Обобщенный класс - это еще более высокий уровень абстракции. С определенной долей упрощения обобщенный класс можно рассматривать как "каркас", на основе которого формируется "обычный" класс, и уже на основе этого "обычного" класса создается объект. Процесс формирования "обычного" класса на основе обобщенного происходит при создании объекта. Как это делается на практике, мы рассмотрим далее.

    Конечно, данная схема очень упрощенная и не совсем точная, но она позволяет понять место и роль обобщенных классов в программном коде.


    Похожим образом идея "обобщенности" распространяется на структуры, интерфейсы и делегаты. Мы последовательно рассмотрим способ реализации данной концепции и начнем с методов и классов.

    На следующем шаге мы рассмотрим обобщенные методы.




Предыдущий шаг Содержание Следующий шаг