Г. Шилдт - Полный справочник по C# (1160789), страница 52
Текст из файла (страница 52)
Он размещается в памяти с помощью конструкторакласса RangeArray. Индекс нижней границы массива сохраняется в закрытой переменной lowerBound, а индекс верхней границы — в закрытойпеременнойupperBound. Затем объявляются переменные элемента, которые поддерживают свойства Length и Error.Конструктор класса RangeArray имеет такой вид:// Создаем массив с заданным размером,public RangeArray(int low, int high) {high++;if(high <= low) {Console.WriteLine("Неверные индексы.");high = 1; // Создаем минимальный массив для// безопасности,low = 0;274Часть I. Язык С #а = new int[high - low];len = high - low;lowerBound = low;upperBound = — h i g h ;Объект типа RangeArray создается в результате передачи нижнего граничного индекса в параметр low и верхнего граничного индекса — в параметр high.
Значениеhigh затем инкрементируется, чтобы вычислить размер массива, поскольку задаваемые индексы изменяются от low до high включительно. После этого проверяем, действительно ли верхний индекс больше нижнего. Если это не так, выводится сообщение об ошибке в индексах и создается одноэлементный массив. Затем выделяется область памяти для массива (либо корректно заданного, либо ошибочно), и ссылка наэту область присваивается переменной а. А переменная l e n (на которой основаносвойство Length) устанавливается равной количеству элементов в массиве.
Наконец,устанавливаются закрытые переменные lowerBound и upperBound.В классе RangeArray затем реализуются свойства Length и Error. Ниже приведены их определения.// Свойство Length, предназначенное только для чтения,p u b l i c i n t Length {get {return len;// Свойство Error, предназначенное только для чтения,public bool Error {get {return errflag;Эти свойства аналогичны свойствам, используемым классом FailSoftArray, и ихработа организована подобным образом.Далее в классе RangeArray реализуется индексатор. Вот его определение:// Это — индексатор для класса RangeArray.public int this[int index] {// Это — get-аксессор.get {if(ok(index)) {errflag = false;return a[index - lowerBound];} else {errflag = true;return 0;// Это — set-аксессор.set {if(ok(index)) {a[index - lowerBound] = value;errflag = false;else errflag = true;Глава 10. Индексаторы и свойства'275IЭтот индексатор очень похож на индексатор класса FailSoftArray, но с однимважным отличием.
Обратите внимание на выражение, которое служит в качестве значения индекса массива а.I index - lowerBoundЭто выражение преобразует реальный индекс, переданный через параметр index,в "нормальный", т.е. в значение, которое имел бы индекс текущего элемента, если быиндексирование массива начиналась с нуля.
Ведь только такое индексирование подходит для базового массива а. Это выражение работает при любом значении переменной lowerBound: положительном, отрицательном или нулевом.Теперь осталось рассмотреть определение метода ok ().// Метод возвращает значение t r u e , если индекс// находится внутри границ,p r i v a t e bool o k ( i n t index) {i f ( i n d e x >= lowerBound & index <= upperBound)return true;return false;Это определение аналогично тому, которое используется в классе FailSoftArrayза исключением того, что попадание индекса в нужный диапазон проверяется с помощью значений переменных lowerBound и upperBound.Класс RangeArray иллюстрирует только один вид массива, создаваемого "под заказ" с помощью индексаторов и свойств.
Можно также создавать динамические массивы (которые расширяются и сокращаются по необходимости), ассоциативные иразреженные массивы. Попробуйте создать один из таких массивов в качестве упражнения.276Часть I. Язык С#Полныйсправочник поНаследованиеНаследование — один из трех фундаментальных принципов объектно-ориентированного программирования, поскольку именно благодаря ему возможно создание иерархических классификаций. Используя наследование, можно создать общийкласс, который определяет характеристики, присущие множеству связанных элементов. Этот класс затем может быть унаследован другими, узкоспециализированнымиклассами с добавлением в каждый из них своих, уникальных особенностей.В языке С# класс, который наследуется, называется базовым.
Класс, который наследует базовый класс, называется производным. Следовательно, производный класс —это специализированная версия базового класса. В производный класс, наследующийвсе переменные, методы, свойства, операторы и индексаторы, определенные в базовом классе, могут быть добавлены уникальные элементы.Основы наследованияС# поддерживает наследование, позволяя в объявление класса встраивать другойкласс.
Это реализуется посредством задания базового класса при объявлении производного. Лучше всего начать с примера. Рассмотрим класс TwoDShape, в котором определяются атрибуты "обобщенной" двумерной геометрической фигуры (например,^квадрата, прямоугольника, треугольника и т.д.).// Класс двумерных объектов,class TwoDShape {public double width;public double height;public void showDimO {Console.WriteLine("Ширина и высота равны " +width + " и " + height);Класс TwoDShape можно использовать в качестве базового (т.е.
как стартовуюплощадку) для классов, которые описывают специфические типы двумерных объектов. Например, в следующей программе класс TwoDShape используется для выведениякласса T r i a n g l e . Обратите внимание на то, как объявляется класс T r i a n g l e .// Простая иерархия классов.using System;// Класс двумерных объектов,class TwoDShape {public double width;public double height;public void showDimO {Console.WriteLine("Ширина и высота равны " +width + " и " + height);// Класс Triangle выводится из класса TwoDShape.class Triangle : TwoDShape {public string style; // Тип треугольника.278Часть I. Язык С#// Метод возвращает площадь треугольника,public double area() {return width * height / 2;// Отображаем тип треугольника,public void showStyleO {Console.WriteLine("Треугольник " + style);c l a s s Shapes {p u b l i c s t a t i c void MainO {T r i a n g l e t l = new T r i a n g l e ( ) ;T r i a n g l e t 2 = new T r i a n g l e ( ) ;t l .
w i d t h = 4.0;t l . h e i g h t - 4.0;t l . s t y l e = "равнобедренный";t 2 . w i d t h = 8.0;t 2 . h e i g h t « 12.0;t 2 . s t y l e = "прямоугольный";Console.WriteLine("Информацияо tl: ");tl.showStyle();tl.showDim();Console.WriteLine ("Площадь равна " + tl.areaO);Console.WriteLine();Console.WriteLine("Информация о t2: " ) ;t2. showStyleO ;t2.showDim();Console.WriteLine("Площадь равна " + t2.area());Вот результаты работы этой программы.Информация о tl:Треугольник равнобедренныйШирина и высота равны 4 и 4Площадь равна 8Информация о t2:Треугольник прямоугольныйШирина и высота равны 8 и 12Площадь равна 4 8В классе T r i a n g l e создается специфический тип объекта класса TwoDShape, вданном случае треугольник.
Класс T r i a n g l e содержит все элементы классаTwoDShape и, кроме того, поле s t y l e , метод area О и метод showStyleO. В переменной s t y l e хранится описание типа треугольника, метод a r e a () вычисляет и возвращает его площадь, а метод showstyle () отображает данные о типе треугольника.Ниже приведен синтаксис, который используется в объявлении класса Triangle,чтобы сделать его производным от класса TwoDShape.class Triangle : TwoDShape {Глава 11. Наследование/279Этот синтаксис можно обобщить. Если один класс наследует другой, то имя базового класса указывается после имени производного, причем имена классов разделяются двоеточием. В С# синтаксис наследования класса очень прост для запоминания ииспользования.Поскольку класс T r i a n g l e включает все члены базового класса, TwoDShape, онможет обращаться к членам width и h e i g h t внутри метода а г е а ( ) .
Кроме того,внутри метода MainO объекты t l и t 2 могут прямо ссылаться на члены width иh e i g h t , как если бы они были частью класса T r i a n g l e . Включение классаTwoDShape в класс T r i a n g l e схематически показано на рис. 11.1.widthTwoDShapeheightshowDim()styleTriangleareashowStyle()Рис. 11.1. Схематическое представлениекласса TriangleНесмотря на то что класс TwoDShape является базовым для класса T r i a n g l e , этосовершенно независимый и автономный класс. То, что его использует в качестве базового производный класс (классы), не означает невозможность использования егосамого. Например, следующий фрагмент кода абсолютно легален:TwoDShape shape = new TwoDShape();shape.width = 10;shape.height = 20;shape.showDim ();Безусловно, объект класса TwoDShape "ничего не знает" и не имеет права доступак классу, производному от TwoDShape.Общая форма объявления класса, который наследует базовый класс, имеет такой вид:class имя__производного_класса : имя_базового_класса {// тело классаДля создаваемого производного класса можно указать только один базовый класс.В С# (в отличие от C++) не поддерживается наследование нескольких базовых классов в одном производном классе.
Этот факт необходимо учитывать при переводеС++-кода на С#. Однако можно создать иерархию наследования, в которой одинпроизводный класс становится базовым для другого производного класса. И конечноже, ни один класс не может быть базовым (ни прямо, ни косвенно) для самого себя.Основное достоинство наследования состоит в том, что, создав базовый класс, который определяет общие атрибуты для множества объектов, его можно использоватьдля создания любого числа более специализированных производных классов. В каждом производном классе можно затем точно "настроить" собственную классификацию. Вот, например, как из базового класса TwoDShape можно вывести производныйкласс, который инкапсулирует прямоугольники:280Часть I. Язык С#// Класс прямоугольников Rectangle, производный/ / о т класса TwoDShape.class Rectangle : TwoDShape {// Метод возвращает значение true, если// прямоугольник является квадратом,public bool isSquareO {if(width == height) return true;return false;// Метод возвращает значение площади прямоугольника,public double area() {return width * height;Класс Rectangle включает класс TwoDShape и добавляет метод i s S q u a r e O , который определяет, является ли прямоугольник квадратом, и метод a r e a О, вычисляющий площадь прямоугольника.Доступ кчленам класса и наследованиеКак разъяснялось в главе 8, члены класса часто объявляются закрытыми, чтобыпредотвратить несанкционированное использование и внесение изменений.
Наследование класса не отменяет ограничения, связанные с закрытым доступом. Таким образом, несмотря на то, что производный класс включает все члены базового класса, онне может получить доступ к тем из НИХ, которые объявлены закрытыми. Например,как показано в следующем коде, если члены width и h e i g h t являются p r i v a t e членами в классе TwoDShape, то класс T r i a n g l e не сможет получить к ним доступ.// Доступ к закрытым членам не наследуется.// Этот пример не скомпилируется.using System;// Класс двумерных объектов,class TwoDShape {double width; // Теперь это private-член.double height; // Теперь это private-член.public void showDim() {Console.WriteLine("Ширина и высота равны " +width + " и " + h e i g h t ) ;// Класс Triangle выводится из класса TwoDShape.class Triangle : TwoDShape {public string style; // Тип треугольника.// Метод возвращает значение площади треугольника,public double area() {return width * height / 2; // Ошибка, нельзя получить// прямой доступ к закрытым// членам.Глава 11.