Шрифт:
ПРИМЕЧАНИЕ Если параметр типа обозначает ссылку или ограничение на базовый класс, то к экземплярам объектов, определяемых таким параметром типа, можно применять операторы == и !=, хотя они проверяют на равенство только ссылки. А для сравнения значений придется реализовать интер фейс IComparable или же обобщенные интерфейсы IComparable и lEquatable. Иерархии обобщенных классов
Обобщенные классы могут входить в иерархию классов аналогично необобщенным классам. Следовательно, обобщенный класс может действовать как базовый или про изводный класс. Главное отличие между иерархиями обобщенных и необобщенных классов заключается в том, что в первом случае аргументы типа, необходимые обоб щенному базовому классу, должны передаваться всеми производными классами вверх по иерархии аналогично передаче аргументов конструктора. Применение обобщенного базового класса
Ниже приведен простой пример иерархии, в которой используется обобщенный базовый класс. // Простая иерархия обобщенных классов. using System; // Обобщенный базовый класс. class Gen<T> { Т ob; public Gen(T о) { ob = о; } // Возвратить значение переменной ob. public Т GetOb { return ob; } } // Класс, производный от класса Gen. class Gen2<T> : Gen<T> { public Gen2(T o) : base(o) { // ... } } class GenHierDemo { static void Main { Gen2<string> g2 = new Gen2<string>("Привет"); Console.WriteLine(g2.GetOb); } }
В этой иерархии класс Gen2 наследует от обобщенного класса Gen. Обратите вни мание на объявление класса Gen2 в следующей строке кода. class Gen2<T> : Gen<T> {
Параметр типа Т указывается в объявлении класса Gen2 и в то же время передается классу Gen. Это означает, что любой тип, передаваемый классу Gen2, будет передавать ся также классу Gen. Например, в следующем объявлении: Gen2<string> g2 = new Gen2<string>("Привет");
параметр типа string передается классу Gen. Поэтому переменная ob в той части класса Gen2, которая относится к классу Gen, будет иметь тип string. Обратите также внимание на то, что в классе Gen2 параметр типа Т не использует ся, а только передается вверх по иерархии базовому классу Gen. Это означает, что в производном классе следует непременно указывать параметры типа, требующиеся его обобщенному базовому классу, даже если этот производный класс не обязательно должен быть обобщенным.
Разумеется, в производный класс можно свободно добавлять его собственные па раметры типа, если в этом есть потребность. В качестве примера ниже приведен ва риант предыдущей иерархии классов, где в класс Gen2 добавлен собственный пара метр типа. // Пример добавления собственных параметров типа в производный класс. using System; // Обобщенный базовый класс. class Gen<T> { Т ob; // объявить переменную типа Т // Передать конструктору ссылку типа Т. public Gen(T о) { ob = о; } // Возвратить значение переменной ob. public Т GetOb { return ob; } } // Класс, производный от класса Gen. В этом классе // определяется второй параметр типа V. class Gen2<T, V> : Gen<T> { V ob2; public Gen2(T о, V o2) : base(о) { ob2 = o2; } public V GetObj2 { return ob2; } } // Создать объект класса Gen2. class GenHierDemo2 { static void Main { // Создать объект класса Gen2 с параметрами // типа string и int. Gen2<string, int> x = new Gen2<string, int>("Значение равно: ", 99); Console.Write(x.GetOb); Console.WriteLine(x.GetObj2); } }
Обратите внимание на приведенное ниже объявление класса Gen2 в данном вари анте иерархии классов. class Gen2<T, V> : Gen<T> {
В этом объявлении Т — это тип, передаваемый базовому классу Gen; а V — тип, ха рактерный только для производного класса Gen2. Он служит для объявления объекта оb2 и в качестве типа, возвращаемого методом GetObj2. В методе Main создается объект класса Gen2 с параметром Т типа string и параметром V типа int. Поэтому код из приведенного выше примера дает следующий результат. Значение равно: 99 Обобщенный производный класс
Необобщенный класс может быть вполне законно базовым для обобщенного про изводного класса. В качестве примера рассмотрим следующую программу. // Пример необобщенного класса в качестве базового для // обобщенного производного класса. using System; // Необобщенный базовый класс. class NonGen { int num; public NonGen(int i) { num = i; } public int GetNum { return num; } } // Обобщенный производный класс. class Gen<T> : NonGen { T ob; public Gen(T о, int i) : base (i) { ob = o; } // Возвратить значение переменной ob. public T GetOb { return ob; } } // Создать объект класса Gen. class HierDemo3 { static void Main { // Создать объект класса Gen с параметром типа string. Gen<String> w = new Gen<String>("Привет", 47); Console.Write(w.GetOb + " "); Console.WriteLine(w.GetNum); } }
Эта программа дает следующий результат. Привет 47
В данной программе обратите внимание на то, как класс Gen наследует от класса NonGen в следующем объявлении. class Gen<T> : NonGen {
Класс NonGen не является обобщенным, и поэтому аргумент типа для него не ука зывается. Это означает, что параметр т, указываемый в объявлении обобщенного про изводного класса Gen, не требуется для указания базового класса NonGen и даже не может в нем использоваться. Следовательно, класс Gen наследует от класса NonGen обычным образом, т.е. без выполнения каких-то особых условий. Переопределение виртуальных методов в обобщенном классе
В обобщенном классе виртуальный метод может быть переопределен таким же об разом, как и любой другой метод. В качестве примера рассмотрим следующую про грамму, в которой переопределяется виртуальный метод GetOb. // Пример переопределения виртуального метода в обобщенном классе. using System; // Обобщенный базовый класс. class Gen<T> { protected Т ob; public Gen(T о) { ob = о; } // Возвратить значение переменной ob. Этот метод является виртуальным. public virtual T GetOb { Console.Write("Метод GetOb из класса Gen" + " возвращает результат: "); return ob; } } // Класс, производный от класса Gen. В этом классе // переопределяется метод GetOb. class Gen2<T> : Gen<T> { public Gen2 (T o) : base(o) { } // Переопределить метод GetOb. public override T GetOb { Console.Write("Метод GetOb из класса Gen2" + " возвращает результат: "); return ob; } } // Продемонстрировать переопределение метода в обобщенном классе. class OverrideDemo { static void Main { // Создать объект класса Gen с параметром типа int. Gen<int> iOb = new Gen<int>(88); // Здесь вызывается вариант метода GetOb из класса Gen. Console.WriteLine(iOb.GetOb); // А теперь создать объект класса Gen2 и присвоить // ссылку на него переменной iOb типа Gen<int>. iOb = new Gen2<int>(99); // Здесь вызывается вариант метода GetOb из класса Gen2. Console.WriteLine(iOb.GetOb); } }