// Обратите внимание, теперь здесь имеется ограничение,
// требующее конструктор по умолчанию.
public class MyList‹T› where T: new() {
private List‹T› listOfData = new List‹T›();
public virtual void PrintList(T data) {}
// Производный тип должен учитывать ограничения базового.
public class MyReadOnlyList‹T›: MyList‹T› where T: new() {
public override void PrintList(T data) {}
}
Если вы только не планируете построить свою собственную библиотеку обобщений
Создание обобщенных интерфейсов
Вы уже видели при рассмотрении пространства имен System.Collections. Generiс, что обобщенные интерфейсы в C# также допустимы (например, IEnumerable‹Т›). Вы, конечно, можете определить свои собственные обобщенные интерфейсы (как с ограничениями, так и без ограничений). Предположим, что нужно определить интерфейс, который сможет выполнять бинарные операции с параметрами обобщенного типа.
public interface IBinaryOperations‹T›
T
T
T
T
}
Известно, что интерфейсы остаются почти бесполезными, пока они не реализованы некоторым классом или структурой. При реализации обобщенного интерфейса поддерживающий его тип указывает тип заполнителя.
public class BasicMath: IBinaryOperations‹int›
public int
public int
public int
public int
}
После этого вы можете использовать BasicMath, как и ожидали.
static void Main(string[] args) {
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
…
}
Исходный код.
Проект 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("Некоторые строковые данные");
// Регистрация цели с помощью