Console.WriteLine("***** Обобщенные интерфейсы *****\n");
BasicMath m = new BasicMath;
Console.WriteLine("1 + 1 = {0}", m.Add(1, 1));
Console.ReadLine;
}
Если вместо этого требуется создать класс BasicMath, действующий на числа с плавающим десятичным разделителем, можно конкретизировать параметр типа так.
public class BasicMath: IBinaryOperations‹double› {
public double Add(double arg1, double arg2) { return arg1 + arg2; }
…
}
Исходный код. Проект GenericInterface размещен в подкаталоге, соответствующем главе 10.
Создание обобщенных делегатов
Наконец, что не менее важно, .NET 2.0 позволяет определять обобщенные типы делегата. Предположим, например, что требуется определить делегат, который сможет вызывать любой метод, возвращающий void и принимающий один аргумент. Если аргумент может меняться, это можно учесть с помощью параметра типа. Для примера рассмотрим следующий программный код (обратите внимание на то, что целевые объекты делегата регистрируются как с помощью "традиционного" синтаксиса делегата, так и с помощью группового преобразования метода).
namespace GenericDelegate {
// Этот обобщенный делегат может вызвать любой метод,
// возвращающий void и принимающий один параметр.
public delegate void MyGenericDelegate‹T›(T arg);
class Program {
static void Main(string[] args) {
Console.WriteLine("***** Обобщенные делегаты *****\n");
// Регистрация цели с помощью 'традиционного'
// синтаксиса делегата.
MyGenericDelegate‹string› strTarget = new MyGenericDelegate‹string›(StringTarget);
strTarget("Некоторые строковые данные");
// Регистрация цели с помощью
// группового преобразования метода.
MyGenericDelegate‹int› intTarget = IntTarget;
intTarget(9);
Console.ReadLine;
}
static void StringTarget(string arg) {
Console.WriteLine("arg в верхнем регистре: {0}", arg.ToUpper);
}
static void IntTarget(int arg) {
Console.WriteLine("++arg: {0}", ++arg);
}
}
}
Обратите внимание на то. что MyGenericDelegate‹T› определяет один пара-метр типа, представляющий аргумент, отправляемый целевому объекту делегата. При создании экземпляра этого типа требуется конкретизировать значение параметра типа, а также имя метода, вызываемого делегатом. Так, если вы укажете строковый тип, то отправите целевому методу строковое значение.
// Создание экземпляра MyGenericDelegate‹T›
// со значением string для параметра типа.
MyGenericDelegate‹string› strTarget = new MyGenericDelegate‹string›(StringTarget);
strTarget("Некоторые строковые данные");
С учетом формата объекта strTarget метод StringTarget должен теперь получить в качестве параметра одну строку.
static void StringTarget(string arg) {
Console.WriteLine("arg в верхнем регистре: {0}", arg.ToUpper);
}
Имитация обобщенных делегатов в .NET 1.1
Как видите, обобщенные делегаты предлагают более гибкий подход для указания вызываемых методов. В рамках .NET 1.1 аналогичного результата можно достичь с помощью базового System.Object.
public delegate void MyDelegate(object arg);
Это позволяет посылать любой тип данных целевому объекту делегата, но при этом не гарантируется типовая безопасность, а кроме того, остаются нерешенными проблемы создания объектных образов. Предположим, например, что мы создали два экземпляра MyDelegate, которые указывают на один и тот же метод MyTarget. Обратите внимание на проблемы создания объектных образов и восстановления значений, а также на отсутствие гарантий типовой безопасности.
class Program {
static void Main(string[] args) {
…
// Регистрация цели с помощью