Полное руководство. С# 4.0
Шрифт:
В силу ограничения ссылочного типа этот вариант интерфейса ISeries нельзя применять к типам значений. Поэтому если реализовать его в рассматриваемом здесь примере программы, то допустимым окажется только объявление ByTwos, но не объявления ByTwos и ByTwos. Сравнение экземпляров параметра типа
Иногда возникает потребность сравнить два экземпляра параметра типа. Допу стим, что требуется написать обобщенный метод IsIn, возвращающий логическое значение true, если в массиве содержится некоторое значение. Для этой цели сначала можно попробовать сделать следующее. // Не годится! public static bool IsIn<T>(T what, T[] obs) { foreach(T v in obs) if(v == what) // Ошибка! return true; return false; }
К сожалению, эта попытка не пройдет. Ведь параметр Т относится к обобщенному типу, и поэтому компилятору не удастся выяснить, как сравнивать два объекта. Требу ется ли для этого поразрядное сравнение или же только сравнение
Для сравнения двух объектов параметра обобщенного типа они должны реализовы вать интерфейс IComparable или IComparable и/или интерфейс IEquatable. В обоих вариантах интерфейса IComparable для этой цели определен метод CompareTo, а в интерфейсе IEquatable — метод Equals. Разновидности интерфейса IComparable предназначены для применения в тех случаях, когда тре буется определить относительный порядок следования двух объектов. А интерфейс IEquatable служит для определения равенства двух объектов. Все эти интерфейсы определены в пространстве имен System и реализованы во встроенных в C# типах дан ных, включая int, string и double. Но их нетрудно реализовать и для собственных создаваемых классов. Итак, начнем с обобщенного интерфейса IEquatable.
Интерфейс IEquatable объявляется следующим образом. public interface IEquatable<T>
Сравниваемый тип данных передается ему в качестве аргумента типа Т. В этом ин терфейсе определяется метод Equals, как показано ниже. bool Equals(Т other)
В этом методе сравниваются взывающий объект и другой объект, определяемый параметром other. В итоге возвращается логическое значение true, если оба объекта равны, а иначе — логическое значение false.
В ходе реализации интерфейса IEquatable обычно требуется также переопре делять методы GetHashCode и Equals(Object), определенные в классе Object, чтобы они оказались совместимыми с конкретной реализацией метода Equals. Ниже приведен пример программы, в которой демонстрируется исправленный вари ант упоминавшегося ранее метода IsIn. // Требуется обобщенный интерфейс IEquatable<T>. public static bool IsIn<T>(T what, T[] obs) where T : IEquatable<T> { foreach(T v in obs) if(v.Equals(what)) // Применяется метод Equals. return true; return false; }
Обратите внимание в приведенном выше примере на применение следующего ограничения. where Т : IEquatable<T>
Это ограничение гарантирует, что только те типы, в которых реализован интерфейс IEquatable, являются действительными аргументами типа для метода IsIn. Вну три этого метода применяется метод Equals, который определяет равенство одного объекта другому.
Для определения относительного порядка следования двух элементов применяется интерфейс IComparable. У этого интерфейса имеются две формы: обобщенная и не обобщенная. Обобщенная форма данного интерфейса обладает преимуществом обе спечения типовой безопасности, и поэтому мы рассмотрим здесь именно ее. Обоб щенный интерфейс IComparable объявляется следующим образом. public interface IComparable<T>
Сравниваемый тип данных передается ему в качестве аргумента типа Т. В этом ин терфейсе определяется метод CompareTo, как показано ниже. int CompareTo(Т other)
В этом методе сравниваются вызывающий объект и другой объект, определяемый параметром other. В итоге возвращается нуль, если вызывающий объект оказывается больше, чем объект other; и отрицательное значение, если вызывающий объект ока зывается меньше, чем объект other.
Для того чтобы воспользоваться методом CompareTo, необходимо указать огра ничение, которое требуется наложить на аргумент типа для реализации обобщенного интерфейса IComparable. А затем достаточно вызвать метод CompareTo, чтобы сравнить два экземпляра параметра типа.
Ниже приведен пример применения обобщенного интерфейса IComparable. В этом примере вызывается метод InRange, возвращающий логическое значение true, если объект оказывается среди элементов отсортированного массива. // Требуется обобщенный интерфейс IComparable<T>. В данном методе // предполагается, что массив отсортирован. Он возвращает логическое // значение true, если значение параметра what оказывается среди элементов // массива, передаваемых параметру obs. public static bool InRange<T>(T what, T[] obs) where T : IComparable<T> { if(what.CompareTo(obs[0]) < 0 || what.CompareTo(obs[obs.Length-1]) > 0) return false; return true; }
В приведенном ниже примере программы демонстрируется применение обоих методов IsIn и InRange на практике. // Продемонстрировать применение обобщенных // интерфейсов IComparable<T> и IEquatable<T>. using System; // Теперь в классе MyClass реализуются обобщенные // интерфейсы IComparable<T> и IEquatable<T>. class MyClass : IComparable<MyClass>, IEquatable<MyClass> { public int Val; public MyClass(int x) { Val = x; } //
Выполнение этой программы приводит к следующему результату. Найдено значение 2. Найден объект MyClass(3). Значение 2 находится в границах массива nums. Значение 1 находится в границах массива nums. Значение 5 находится в границах массива nums. Значение 0 НЕ находится в границах массива nums Значение 6 НЕ находится в границах массива nums Объект MyClass(2) находится в границах массива nums. Объект MyClass(1) находится в границах массива nums. Объект MyClass(4) находится в границах массива nums. Объект MyClass(0) НЕ находится в границах массива nums. Объект MyClass(5) НЕ находится в границах массива nums.
ПРИМЕЧАНИЕ Если параметр типа обозначает ссылку или ограничение на базовый класс, то к экземплярам объектов, определяемых таким параметром типа, можно применять операторы == и !=, хотя они проверяют на равенство только ссылки. А для сравнения значений придется реализовать интер фейс 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); } }