Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ответы ОСиСП.doc
Скачиваний:
71
Добавлен:
11.05.2015
Размер:
1.78 Mб
Скачать

Обобщенные делегаты

Поддержка обобщенных делегатов в CLR позволяет передавать методам обратного вызова любые типы объектов, обеспечивая при этом безопасность типов. Благодаря обобщенным делегатам экземпляры значимого типа могут передаваться методам обратного вызова без упаковки. Как уже говорилось в главе 15, делегат — это просто определение класса с помощью четырех методов: конструктора и методов Invoke, Beginlnvoke и Endlnvoke. При определении типа-делегата с параметрами-типами, компилятор определяет методы класса делегата, а параметры-типы применяются ко всем методам, параметры и возвращаемые значения которых относятся к указанному параметру-типу. Например, обобщенный делегат определяется следующим образом:

public delegate TReturn CallMe<TReturn, TKey, TValue>(TKey key, TValue value);

Компилятор превращает его в класс, который логически выглядит так:

public sealed class CallMe<TReturn, TKey, TValue> : MulticastDelegate

{

public CallMe(Object _object, IntPtr method);

public TReturn Invoke(TKey key, TValue value);

public IAsyncResult BeginInvoke(TKey key, TValue value,

AsyncCallback callback, Object _object);

public TReturn EndInvoke(IAsyncResult result);

}

В составе FCL есть много обобщенных типов-делегатов. Большинство из них используется при работе с наборами. Например:

// Обычно служит для выполнения действия над элементом набора.

public delegate void Action<T>(T obj);

// Обычно используется при сравнении элементов двух наборов в целях сортировки.

public delegate Int32 Comparison<T>(T x, T y);

// Обычно служит для преобразования типа элемента набора.

public delegate TOutput Converter<TInput, TOutput>(TInput input);

// Обычно применяется, чтобы узнать, прошел ли элемент набора тест.

public delegate Boolean Predicate<T>(T obj);

Следующий обобщенный делегат, поставляемый с FCL, используется для событий:

public delegate void EventHandler<TEventArgs>(

object sender, TEventArgs e) where TEventArgs : EventArgs;

Оператор where в этом примере называют ограничением.

Обобщенные методы

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

internal sealed class GenericType<T>

{

private T m_value;

public GenericType(T value) { m_value = value; }

public TOutput Converter<TOutput>()

{

TOutput result = (TOutput)Convert.ChangeType(m_value, typeof(TOutput));

return result;

}

}

internal sealed class GenericType<T>

{

private T m_value;

public GenericType(T value)

{

m_value = value;

}

public TOutput Converter<TOutput>()

{

TOutput result = (TOutput)Convert.ChangeType(m_value, typeof(TOutput));

return result;

}

Здесь в классе GenericType определяется собственный параметр-тип (Т), а в методе Converter — собственный параметр-тип (TOutput). Благодаря этому можно создать класс GenericType, работающий с любым типом. Метод Converter преобразует объект, на который ссылается поле m_value, в другие типы в зависимости от аргумента-типа, переданного ему при его вызове. Наличие параметров-типов и параметров метода дает небывалую гибкость. Удачный пример обобщенного метода — метод Swap:

private static void Swap<T>(ref T o1, ref T o2)

{

T temp = o1;

o1 = o2;

o2 = temp;

}

//Теперь вызывать Swap из кода можно следующим образом:

private static void CallingSwap()

{

Int32 n1 = 1, n2 = 2;

Console.WriteLine("n1={0}, n2={1}", n1, n2);

Swap<Int32>(ref n1, ref n2);

Console.WriteLine("n1={0}, n2={1}", n1, n2);

String s1 = "Aidan", s2 = "Kristin";

Console.WriteLine("s1={0}, s2={1}", s1, s2);

Swap<String>(ref s1, ref s2);

Console.WriteLine("s1={0}, s2={1}", s1, s2);

}

Использование обобщенных типов с методами, принимающими параметры out и ref особенно интересно тем, что переменные, передаваемые в качестве аргумента out/ref, должны быть того же типа, что и параметр метода, чтобы избежать возможных нарушений безопасности типов. В сущности, именно поэтому методы Exchange и Compare-Exchange класса Interlocked поддерживают обобщенную перегрузку:

public static class Interlocked

{

public static T String<T>(ref T locationl, T value) where T : class;

public static T CompareExchange<T>(ref T locationl, T value, T comparand) where T : class;

}