Универсальные методы (Руководство по программированию на C#)

Обновлен: Ноябрь 2007

Универсальный метод – это метод, объявляемый с параметрами типа, как в следующем примере.

static void Swap<T>(ref T lhs, ref T rhs)
{
    T temp;
    temp = lhs;
    lhs = rhs;
    rhs = temp;
}

Следующий пример кода демонстрирует один способ вызова метода при помощи int для аргумента типа.

public static void TestSwap()
{
    int a = 1;
    int b = 2;

    Swap<int>(ref a, ref b);
    System.Console.WriteLine(a + " " + b);
}

Аргумент типа можно опустить и компилятор определит его. Следующий вызов Swap эквивалентен предыдущему вызову.

Swap(ref a, ref b);

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

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

class SampleClass<T>
{
    void Swap(ref T lhs, ref T rhs) { }
}

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

class GenericList<T>
{
    // CS0693
    void SampleMethod<T>() { }
}

class GenericList2<T>
{
    //No warning
    void SampleMethod<U>() { }
}

Чтобы с параметрами типа в методах можно было выполнять более специализированные операции, используйте ограничения. Текущую версию Swap<T>, которая теперь имеет имя SwapIfGreater<T>, можно использовать только с аргументами типа, реализующими IComparable<T>.

void SwapIfGreater<T>(ref T lhs, ref T rhs) where T : System.IComparable<T>
{
    T temp;
    if (lhs.CompareTo(rhs) > 0)
    {
        temp = lhs;
        lhs = rhs;
        rhs = temp;
    }
}

Универсальные методы можно перегрузить в нескольких параметрах типа. Например, все следующие методы можно разместить в одном и том же классе.

void DoWork() { }
void DoWork<T>() { }
void DoWork<T, U>() { }

Спецификация языка C#

Дополнительные сведения см. в разделе Спецификация языка C#.

См. также

Основные понятия

Руководство по программированию в C#

Ссылки

Введение в универсальные шаблоны. (Руководство по программированию на C#)

System.Collections.Generic

Методы (Руководство по программированию на C#)