Следует также понимать то, что при таком ограничении метод Swap() уже не сможет переставлять строковые типы (поскольку они являются ссылочными).
Отсутствие поддержки ограничений при использовании операций
При создании обобщенных методов для вас может оказаться сюрпризом появление ошибок компилятора, когда с параметрами типа используются операции C# (+, -, *, == и т.д.). Например, я уверен, вы сочли бы полезными классы Add(), Subtract(), Multiply() и Divide(), способные работать с обобщенными типами.
// Ошибка компиляции!
// Нельзя применять операции к параметрам типа!
public class BasicMath‹T› {
public T Add(T arg1, T arg2) { return arg1 + arg2; }
public T Subtract(T arg1, T arg2) { return arg1 – arg2; }
public T Multiply(T arg1, T arg2) { return arg1 * arg2; }
public T Divide(T arg1, T arg2) { return arg1 / arg2; }
}
Как ни печально, этот класс BasicMath‹T› не компилируется. Это может показаться большим ограничением, но не следует забывать, что обобщения являются
// Только для иллюстрации!
// Этот программный код не является допустимым в C# 2.0.
public class BasicMath‹T› where T: operator +, operator -, operator *, operator / {
public T Add(T arg1, T arg2) { return arg1 + arg2; }
public T Subtract(T arg1, T arg2) { return arg1 – arg2; }
public T Multiply(T arg1, T arg2) { return arg1 * arg2; }
public T Divide(T arg1, T arg2) { return arg1 / arg2; }
}
Увы, ограничения обобщенных типов при использовании операций в C# 2005 не поддерживаются.
Исходный код. Проект CustomGenericCollection размещен в подкаталоге, соответствующем главе 10.
Создание обобщенных базовых классов
Перед рассмотрением обобщенных интерфейсов следует указать на то, что обобщенные классы могут быть базовыми для других классов и могут таким образом определять любое число виртуальных и абстрактных методов. Однако производные типы должны подчиняться определенным правилам, вытекающим из природы обобщенной абстракции. Во-первых, если обобщенный класс расширяется необобщенным, то производный класс должен конкретизировать параметр типа,
// Предположим, что создан пользовательский
// обобщенный класс списка.
public class MyList‹T› {
private List‹T› listOfData = new List‹T›();
}
// Конкретные типы должны указать параметр типа,
// если они получаются из обобщенного базового класса.
public class MyStringList: MyList‹string› {}
Кроме того, если обобщенный базовый класс определяет обобщенные виртуальные или абстрактные методы, производный тип должен переопределить эти обобщенные методы, используя конкретизированный параметр типа.
// Обобщенный класс с виртуальным методом.
public class MyList‹T› {
private List‹T› listOfData = new List‹T›();
public virtual void PrintList(T data) {}
}
public class MyStringList: MyList‹string› {
// В производных методах нужно заменить параметр типа,
// используемый а родительском классе.
public override void PrintList(string data) {}
}
Если производный тип тоже является обобщенным, дочерний класс может (опционально) использовать заменитель типа в своем определении. Однако знайте, что любые ограничения, размещенные в базовом классе, должны "учитываться" и производным типом. Например: