Построение специальных обобщенных методов представляет собой более развитую версию традиционной перегрузки методов. В главе 2 вы узнали, что перегрузка — это действие по определению нескольких версий одного метода, которые отличаются друг от друга количеством или типами параметров.
Хотя перегрузка является полезным средством объектно-ориентированного языка, проблема заключается в том, что при этом довольно легко получить в итоге огромное количество методов, которые по существу делают одно и то же. Например, пусть необходимо создать методы, которые позволяют менять местами два фрагмента данных посредством простой процедуры. Вы можете начать с написания нового статического класса с методом, который
способен оперировать целочисленными значениями:
using System;
namespace CustomGenericMethods
{
static class SwapFunctions
{
// Поменять местами два целочисленных значения.
static void Swap(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}
}
}
Пока все идет хорошо. Но теперь предположим, что нужно менять местами также и два объекта
Person
; действие потребует написания новой версии метода
Swap
:
// Поменять местами два объекта Person.
static void Swap(ref Person a, ref Person b)
{
Person temp = a;
a = b;
b = temp;
}
Вне всяких сомнений вам должно быть ясно, чем все закончится. Если также понадобится менять местами два значения с плавающей точкой, два объекта растровых изображений, два объекта автомобилей, два объекта кнопок или что-нибудь еще, то придется писать дополнительные методы, что в итоге превратится в настоящий кошмар при сопровождении. Можно было бы построить один (необобщенный) метод, оперирующий с параметрами типа
object
, но тогда возвратятся все проблемы, которые были описаны ранее в главе, т.е. упаковка, распаковка, отсутствие безопасности в отношении типов, явное приведение и т.д.
Наличие группы перегруженных методов, отличающихся только входными аргументами — явный признак того, что обобщения могут облегчить ситуацию. Рассмотрим следующий обобщенный метод
Swap<T>
, который способен менять местами два значения типа
Т
:
// Этот метод будет менять местами два элемента
// типа, указанного в параметре <Т>.
static void Swap<T>(ref T a, ref T b)
{
Console.WriteLine("You sent the Swap method a {0}", typeof(T));
T temp = a;
a = b;
b = temp;
}
Обратите внимание, что обобщенный метод определен за счет указания параметра типа после имени метода, но перед списком параметров. Здесь заявлено, что метод
Swap<T>
способен оперировать на любых двух параметрах
типа
<Т>
. Для придания некоторой пикантности имя замещаемого типа выводится на консоль с использованием операции
typeof
языка С#. Взгляните на показанный ниже вызывающий код, который меняет местами целочисленные и строковые значения:
Console.WriteLine("***** Fun with Custom Generic Methods *****\n");
Главное преимущество такого подхода в том, что придется сопровождать только одну версию
Swap<T>
, однако она в состоянии работать с любыми двумя элементами заданного типа в безопасной в отношении типов манере. Еще лучше то, что находящиеся в стеке элементы остаются в стеке, а расположенные в куче — соответственно в куче.
Выведение параметров типа
При вызове обобщенных методов вроде
Swap<T>
параметр типа можно опускать, если (и только если) обобщенный метод принимает аргументы, поскольку компилятор в состоянии вывести параметр типа на основе параметров членов. Например, добавив к операторам верхнего уровня следующий код, можно менять местами два значения