Поиск по сайту:

Как T работает в C#? Как использовать общие параметры типа


В C# параметр «T» часто используется для определения функций, которые принимают любой тип. Они используются для написания универсальных классов и методов, которые могут работать с любыми данными, сохраняя при этом строгую безопасность типов. Мы обсудим, как они работают и как их использовать.

Как работает ?

Переменная «T», которую вы, вероятно, видели в некоторых определениях методов, называется параметром универсального типа или просто «универсальным». Универсальные методы, использующие T, можно использовать с любым типом, что упрощает определение классов и методов, которые не заботятся об обрабатываемых данных, но хотят их сохранить.

Например, в коллекциях используются дженерики, чтобы они могли обрабатывать все, что пользователь бросает в них. Для List и List нет другого определения; вместо этого есть одно определение для List.

На практике это выглядит следующим образом. Несмотря на то, что это класс, а не метод, вы все равно можете передать ему параметры типа, используя синтаксис скобок . Затем везде, где вам нужно сослаться на что-то такого типа, вы просто подставляете вместо этого параметр.

Помещение любого значения в скобки <> позволяет использовать это имя вместо допустимого типа в любом месте определения класса или метода, использующего этот параметр. Если вы представляете себе GenericList, везде, где вы пишете int как тип, вы вместо этого пишете T и оставляете это на усмотрение. пользователю сообщить этому классу, какой тип использовать.

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

На самом деле вы можете назвать эту переменную T как угодно, хотя общепринятой практикой является, по крайней мере, начинать ее с «T». Если у вас есть функция, которой нужны аргументы нескольких типов, вы можете назвать их по-разному, например «TOutput» или «TInput». Это часто используется в определениях делегатов и в словарях, где у вас есть TKey и TValue.

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

Примечательно, что вы можете использовать параметр типа в фактических параметрах функции. Вам все равно нужно будет поместить его в квадратные скобки <>, иначе это будет просто недопустимый тип, но вы можете использовать параметр в любом месте определения этого метода.

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

Ограничения типа

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

Это делается с помощью синтаксиса where T : . Простейшей формой этого является where T : ClassName, что гарантирует, что параметр T должен быть заданным типом ClassName или производным от него. Это обеспечивает типобезопасный полиморфизм, такой как эта функция, которая принимает любой тип Fruit и возвращает List, а не List, который быть технически правильным, но потерять ценную информацию о типе.

Вы можете видеть, что если мы попытаемся использовать эту функцию с чем-то, что не является Fruit, компилятор будет кричать на вас.

Помимо простого наследования, есть еще несколько полезных ограничений:

  • where T : InterfaceName – аналогично T : ClassName , но гарантирует, что аргумент типа реализует данный интерфейс.
  • где T : класс – гарантирует, что аргумент типа является ссылочным типом.
  • где T : struct – гарантирует, что аргумент типа является типом значения, не допускающим значение NULL.
  • где T : notnull – тип аргумента должен быть ненулевым.
  • где T : new() – аргумент типа должен иметь возможность построения без параметров.
  • где T : TOther – аргумент типа T должен быть или производным от аргумента типа TOther.

Вы можете указать несколько ограничений в списке, разделенном запятыми.