синтаксис, что и для объявления обобщенного класса.
А теперь обратите внимание на следующее объявление класса ByTwos, реализующего интерфейс I series.
class ByTwos<T> : ISeries<T> {
Параметр типа Т указывается не только при объявлении класса ByTwos, но и при объявлении интерфейса ISeries. И это очень важно. Ведь класс, реализующий обобщенный вариант интерфейса, сам должен быть обобщенным. Так, приведенное ниже объявление недопустимо, поскольку параметр типа Т не определен.
class ByTwos : ISeries<T> { // Неверно!
Аргумент типа, требующийся для интерфейса ISeries, должен быть передан классу ByTwos. В противном случае интерфейс никак не сможет получить аргумент типа.
Далее переменные, хранящие текущее значение в последовательном ряду (val) и его начальное значение (start), объявляются как объекты обобщенного типа Т. После этого объявляется делегат IncByTwo. Этот делегат определяет форму метода, используемого для увеличения на два значения, хранящегося в объекте типа Т. Для того чтобы в классе ByTwos могли обрабатываться данные любого типа, необходимо каким-то образом определить порядок увеличения на два значения каждого типа данных. Для этого конструктору класса ByTwos передается ссылка на метод, выполняющий увеличение на два. Эта ссылка хранится в переменной экземпляра делегата incr. Когда требуется сгенерировать следующий элемент в последовательном ряду, этот метод вызывается с помощью делегата incr.
А теперь обратите внимание на класс ThreeD. В этом классе инкапсулируются координаты трехмерного пространства (X,Z,Y). Его назначение — продемонстрировать обработку данных типа класса в классе ByTwos.
Далее в классе Genlntf Demo объявляются три метода увеличения на два для объектов типа int, double и ThreeD. Все эти методы передаются конструктору класса ByTwos при создании объектов соответствующих типов. Обратите особое внимание на приведенный ниже метод ThreeDPlusTwo ().
// Определить метод увеличения на два каждого // последующего значения координат объекта типа ThreeD. static ThreeD ThreeDPlusTwo(ThreeD v) { if(v==null) return new ThreeD(0, 0, 0); else return new ThreeD(v.x + 2, v.y +
}
В этом методе сначала проверяется, содержит ли переменная экземпляра v пустое значение (null). Если она содержит это значение, то метод возвращает новый объект типа ThreeD со всеми обнуленными полями координат. Ведь дело в том, что переменной v по умолчанию присваивается значение типа default (Т) в конструкторе класса ByTwos. Это значение оказывается по умолчанию нулевым для типов значений и пустым для типов ссылок на объекты. Поэтому если предварительно не был вызван метод SetStart(),TO перед первым увеличением на два переменная v будет содержать пустое значение вместо ссылки на объект. Это означает, что для первого увеличения на два требуется новый объект.
На параметр типа в обобщенном интерфейсе могут накладываться ограничения таким же образом, как и в обобщенном классе. В качестве примера ниже приведен вариант объявления интерфейса ISeries с ограничением на использование только ссылочных типов.
public interface ISeries<T> where T : class {
Если реализуется именно такой вариант интерфейса ISeries, в реализующем его классе следует указать то же самое ограничение на параметр типа Т, как показано ниже.
class ByTwos<T> : ISeries<T> where T : class {
В силу ограничения ссылочного типа этот вариант интерфейса ISeries нельзя применять к типам значений. Поэтому если реализовать его в рассматриваемом здесь примере программы, то допустимым окажется только объявление ByTwos<ThreeD>, но не объявления ByTwos<int> и ByTwos<double>.
Сравнение экземпляров параметра типа
Иногда возникает потребность сравнить два экземпляра параметра типа. Допустим, что требуется написать обобщенный метод Is In (), возвращающий логическое значение true, если в массиве содержится некоторое значение. Для этой цели сначала можно попробовать сделать следующее.
// Не годится!
public static bool IsIn<T>(T what, T[] obs) { foreach(T v in obs)
if(v == what) // Ошибка! return true;
return false;
}
К сожалению, эта попытка не пройдет. Ведь параметр Т
