Учебное пособие (1077022), страница 17
Текст из файла (страница 17)
Правила формирования возвращаемыхзначений определяются интерфейсом IComparable, метод CompareToвозвращает -1, 0, 1 потому что он должен делать это в соответствии стребованиями разработчиков интерфейса IComparable.Врассмотренномпримеренеобходимоотметитьнескольконеочевидную комбинацию использования абстрактных и конкретныхклассов и методов. Абстрактный класс «Геометрическая фигура» содержитабстрактный метод Area, который должен быть реализован в классахнаследниках. Напомним, что абстрактный метод автоматически являетсявиртуальным. Метод Area вызывается в методе CompareTo, которыйявляется конкретным методом (содержит реализацию), при этом методCompareTo также объявлен в абстрактном классе «Геометрическаяфигура».В классах, наследуемых от абстрактного класса «Геометрическаяфигура», необходимо реализовать метод Area, который рассчитываетплощадь для конкретного типа фигуры (например, прямоугольника,окружности).
А метод CompareTo реализовывать не нужно, его реализацияавтоматически наследуется из абстрактного класса «Геометрическаяфигура». При этом метод CompareTo будет автоматически использовать140реализацию метода Area из текущего класса, потому что абстрактныйметод Area автоматически является виртуальным.Пример класса «Геометрическая фигура» с учетом реализацииметода сравнения:usingusingusingusingSystem;System.Collections.Generic;System.Linq;System.Text;namespace FigureCollections{/// <summary>/// Класс фигура/// </summary>abstract class Figure : IComparable{/// <summary>/// Тип фигуры/// </summary>public string Type{get{return this._Type;}protected set{this._Type = value;}}string _Type;/// <summary>/// Вычисление площади/// </summary>/// <returns></returns>public abstract double Area();/// <summary>/// Приведение к строке, переопределение метода Object/// </summary>/// <returns></returns>public override string ToString(){return this.Type + " площадью " + this.Area().ToString();}////////////<summary>Сравнение элементов (для сортировки)this - левый параметр сравнения</summary>141/// <param name="obj">правый параметр сравнения</param>/// <returns>/// -1 - если левый параметр меньше правого/// 0 - параметры равны/// 1 - правый параметр меньше левого/// </returns>public int CompareTo(object obj){//Приведение параметра к типу "фигура"Figure p = (Figure)obj;//Сравнениеif (this.Area() < p.Area()) return -1;else if (this.Area() == p.Area()) return 0;else return 1; //(this.Area() > p.Area())}}}Наследование класса Figure от интерфейса IComparable являетсяобязательным.ЕслипростореализоватьметодCompareToбезнаследования от интерфейса, то ошибка при сортировке все равно будетвозникать.
Можно сделать вывод, что метод Sort проверяет не простоналичие у класса элемента списка метода CompareTo (с помощью неявнойили «утиной» типизации), а проверяет факт наследования класса элементасписка от интерфейса IComparable. В .NET такие проверки реализуютсядостаточно просто с помощью механизма рефлексии.Отметим,чтоиспользованиеинтерфейсаIComparableимеетсущественное ограничение – метод CompareTo может быть реализованединственным образом.
То есть для класса данных можно задать толькоодну метрику сравнения, которая «встраивается» в класс данных.Например, в технологии LINQ для решения аналогичной задачипредложен более гибкий подход на основе классов-компараторов, которыереализуются «поверх» классов данных, что позволяет создавать несколькоклассов-компараторов для одного класса данных.После наследования класса Figure от интерфейса IComparable методсортировки может быть вызван без возникновения исключений:List<Figure> fl = new List<Figure>();fl.Add(circle);fl.Add(rect);142fl.Add(square);Console.WriteLine("\nПеред сортировкой:");foreach (var x in fl) Console.WriteLine(x);//сортировкаfl.Sort();Console.WriteLine("\nПосле сортировки:");foreach (var x in fl) Console.WriteLine(x);Результаты вывода в консоль:Перед сортировкой:Круг площадью 78,5398163397448Прямоугольник площадью 20Квадрат площадью 25После сортировки:Прямоугольник площадью 20Квадрат площадью 25Круг площадью 78,5398163397448Следовательно, для сортировки коллекций, содержащих объектыклассов, необходимо реализовывать в данных классах интерфейсIComparable.6.2 Созданиенестандартнойколлекциинаосновестандартной коллекцииВ том случае, когда необходимой стандартной коллекции вбиблиотеке нет, прикладному программисту нужно самостоятельнореализовывать требуемую коллекцию.В этом случае возможны два подхода:1.
Использовать стандартную коллекцию и написать «надстройку»над ней для реализации требуемого поведения. Данный способпредпочтителен, так как требует относительно небольших ресурсовпри кодировании.2. Если предыдущий подход невозможен, то приходится создаватьколлекцию полностью с нуля.143В данном разделе рассмотрен первый подход на примере класса«Разреженная матрица».Разреженная матрица – тип данных, который достаточно частоиспользуется в информатике.
Это матрица большой размерности, котораяможет быть заполнена на 3-5%, остальные ячейки матрицы пусты.Использование в этом случае обычной матрицы нецелесообразно, так какпотребует выделения очень больших объемов памяти для хранения пустыхзначений.Разреженную матрицу можно реализовать «поверх» стандартнойколлекции.Вприведенномнижепримереразреженнаяматрицареализована на основе словаря. Ключом элемента словаря являетсякомбинация индексов ячейки матрицы по строке и столбцу, значениемэлемента словаря – значение элемента матрицы.Класс «Разреженная матрица» реализован в виде обобщеннойколлекции, класс-обобщение T соответствует типу ячейки матрицы.Пример реализации класса «Разреженная матрица»:usingusingusingusingSystem;System.Collections.Generic;System.Linq;System.Text;namespace FigureCollections{public class Matrix<T>{/// <summary>/// Словарь для хранения значений/// </summary>Dictionary<string, T> _matrix = new Dictionary<string, T>();/// <summary>/// Количество элементов по горизонтали (максимальноеколичество столбцов)/// </summary>int maxX;/// <summary>/// Количество элементов по вертикали (максимальноеколичество строк)144/// </summary>int maxY;/// <summary>/// Реализация интерфейса для проверки пустого элемента/// </summary>IMatrixCheckEmpty<T> сheckEmpty;/// <summary>/// Конструктор/// </summary>public Matrix(int px, int py,IMatrixCheckEmpty<T> сheckEmptyParam){this.maxX = px;this.maxY = py;this.сheckEmpty = сheckEmptyParam;}/// <summary>/// Индексатор для доступа к данных/// </summary>public T this[int x, int y]{set{CheckBounds(x, y);string key = DictKey(x, y);this._matrix.Add(key, value);}get{CheckBounds(x, y);string key = DictKey(x, y);if (this._matrix.ContainsKey(key)){return this._matrix[key];}else{return this.сheckEmpty.getEmptyElement();}}}/// <summary>/// Проверка границ/// </summary>void CheckBounds(int x, int y){145if (x < 0 || x >= this.maxX){throw new ArgumentOutOfRangeException("x","x=" + x + " выходит за границы");}if (y < 0 || y >= this.maxY){throw new ArgumentOutOfRangeException("y","y=" + y + " выходит за границы");}}/// <summary>/// Формирование ключа/// </summary>string DictKey(int x, int y){return x.ToString() + "_" + y.ToString();}/// <summary>/// Приведение к строке/// </summary>/// <returns></returns>public override string ToString(){StringBuilder b = new StringBuilder();for (int j = 0; j < this.maxY; j++){b.Append("[");for (int i = 0; i < this.maxX; i++){//Добавление разделителя-табуляцииif (i > 0){b.Append("\t");}//Если текущий элемент не пустойif (!this.сheckEmpty.checkEmptyElement(this[i, j])){//Добавить приведенный к строке текущий элементb.Append(this[i, j].ToString());}else{//Иначе добавить признак пустого значенияb.Append(" - ");}}b.Append("]\n");146}return b.ToString();}}}Рассмотрим работу данного класса более подробно.
Основнаяструктура данных для хранения разреженной матрицы – словарь _matrix.Ключ словаря – строка, которая содержит комбинацию индексов ячейкиматрицы по строке и столбцу, значение словаря – обобщенный тип Т,который является типом значения элемента матрицы.Поля данных maxX и maxY используются для хранения размеровматрицы.Поле сheckEmpty содержит объект класса, реализующего интерфейсIMatrixCheckEmpty<T> для работы с пустыми значениями.Пример интерфейса IMatrixCheckEmpty:using System;namespace FigureCollections{/// <summary>/// Проверка пустого элемента матрицы/// </summary>public interface IMatrixCheckEmpty<T>{/// <summary>/// Возвращает пустой элемент/// </summary>T getEmptyElement();/// <summary>/// Проверка что элемент является пустым/// </summary>bool checkEmptyElement(T element);}}Интерфейс содержит два метода: getEmptyElement, возвращающий пустой элемент разреженнойматрицы;147 checkEmptyElement, проверяющий, что переданное в качествепараметраметодазначениеявляетсяпустымэлементомматрицы.Методы данного интерфейса используются при создании разреженнойматрицы.Вернемся к классу разреженной матрицы Matrix<T>.















