Г. Шилдт - Полный справочник по C# (1160789), страница 49
Текст из файла (страница 49)
Например, инструкция|= 19;работает следующим образом. Сначала выполняется оператор преобразования i n t значения в Nybble-объект. При этом создается новый Nybble-объект, который содержит младшие четыре (двоичных) разряда числа 19 (1910 = 100112). Это приводит кпереполнению, поскольку значение 19 выходит за пределы диапазона, допустимого вклассе Nybble. В результате получаем число 3 (00112 = 310), которое и присваиваетсяобъекту а. Без определения операторов преобразования такие выражения были бы недопустимы.Преобразование Nybble-объекта в int-значение используется также в цикле for.Без определения этого преобразования было бы невозможно написать цикл for такимпростым способом.Глава 9. Перегрузка операторов255Полныйсправочник поИндексаторы исвойстваВэтой главе рассматриваются два специальных типа членов класса, которые тесно связаны друг с другом: индексаторы и свойства.
Каждый из этих типов расширяет возможности класса, усиливая его интеграцию в систему типов языка С# игибкость. Индексаторы обеспечивают механизм, посредством которого к объектамможно получать доступ по индексу подобно тому, как это реализовано в массивах.Свойства предлагают эффективный способ управления доступом к данным экземпляра класса. Эти типы связаны друг с другом, поскольку оба опираются на еще односредство С#: аксессор, или средство доступа к данным.ИндексаторыКак вы знаете, индексация массивов реализуется с использованием оператора" [ ] " . В своих классах вы можете перегрузить его, но не прибегая к "услугам" методаo p e r a t o r (), а посредством создания индексатора (indexer).
Индексатор позволяетобеспечить индексированный доступ к объекту. Главное назначение индексаторов —поддержать создание специализированных массивов, на которые налагается одно илинесколько ограничений. При этом индексаторы можно использовать в синтаксисе,подобном реализованному в массивах. Индексаторы могут характеризоваться однойили несколькими размерностями, но мы начнем с одномерных индексаторов.Создание одномерных индексаторовОдномерный индексатор имеет следующий формат.тип_элемента this[int индекс] {// Аксессор считывания данных,get {// Возврат значения, заданного// элементом индекс.// Аксессор установки данных,set {// Установка значения, заданного// элементом индекс.Здесьтип_элемента —базовыйтипиндексатора.Такимобразом,тип_элемента — это тип каждого элемента, к которому предоставляется доступ посредством индексатора.
Он соответствует базовому типу массива. Параметр индексполучает индекс опрашиваемого (или устанавливаемого) элемента. Строго говоря,этот параметр не обязательно должен иметь тип i n t , но поскольку индексаторыобычно используются для обеспечения индексации массивов, целочисленный тип —наиболее подходящий.В теле индексатора определяются два аксессора (средства доступа) с именами get иs e t . Аксессор подобен методу за исключением того, что в нем отсутствует объявлениетипа возвращаемого значения и параметров. При использовании индексатора аксессоры вызываются автоматически, и в качестве параметра оба аксессора принимают индекс.
Если индексатор стоит слева от оператора присваивания, вызывается аксессорs e t и устанавливается элемент, заданный параметром индекс. В противном случаевызывается аксессор get и возвращается значение, соответствующее параметру инГлава 10. Индексаторы и свойства257деке. Метод s e t также получает значение (именуемое value), которое присваиваетсяэлементу массива, найденному по заданному индексу.Одно из достоинств индексатора состоит в том, что он позволяет точно управлятьхарактером доступа к массиву, "отбраковывая" попытки некорректного доступа. Рассмотрим пример.
В следующей программе класс F a i l S o f t A r r a y реализует массив,который "вылавливает" ошибки нарушения границ, предотвращая возникновение исключительных ситуаций. Это достигается за счет инкапсуляции массива как закрытого члена класса и осуществления доступа к этому массиву только через индексатор.При таком подходе можно предотвратить любую попытку получить доступ к массивуза пределами его границ, причем последствия попытки нарушить границы в этом случае можно сравнить с "мягкой посадкой", а не с "катастрофическим падением".
Поскольку в классе F a i l S o f t A r r a y используется индексатор, к массиву можно обращаться с помощью обычной формы записи ([ ]).// Использование индексатора для создания// отказоустойчивого массива.using System;fclass FailSoftArray {int[]a;// Ссылка на массив.public int Length; // Length - открытый член.public bool errflag; // Индикатор результата// последней операции.// Создаем массив заданного размера,public FailSoftArray(int size) {a = new int[size];Length = size;}// Это - индексатор для класса FailSoftArray.public int this[int index] {// Это - get-аксессор.get {if(ok(index)) {errflag = false;return a[index];} else {errflag = true;return 0;// Это - set-аксессор.set {if(ok(index)) {a[index] = value;errflag = false;}else errflag = true;//258Метод возвращает значение true, еслиЧасть I. Язык С#// индекс - в пределах границ,private bool ok(int index) {if(index >= 0 & index < Length) return true;return false;// Демонстрируем отказоустойчивый массив,class FSDemo {public static void Main() {FailSoftArray fs = new FailSoftArray(5);int x;// Вот как выглядит "мягкая посадка" при ошибках.Console.WriteLine("Мягкое приземление.
") ;for(int i=0; i < (fs.Length * 2) ;fs[i] = i*10;for(int i=0; i < (fs.Length * 2 ) ;x = fs[i];if(x != -1) Console.Write(x + " ") ;}Console.WriteLine();// Теперь генерируем некорректный доступ.Console.WriteLine("ХпРабота с уведомлением об ошибках.");for(int i=0; i < (fs.Length * 2 ) ; i++) {fs[i] * i*10;if(fs.errflag)Console.WriteLine("fs[" + i + "] вне границ");for(int i=0; i < (fs.Length * 2 ) ;x = fs[i] ;if(!fs.errflag) Console.Write(x + " " ) ;elseConsole.WriteLine("fs[" + i + "] вне границ");При выполнении этой программы получаем такие результаты:"Мягкое приземление".О 10 20 30 40 О О О О ОРабота с уведомлением об ошибках.fs[5] вне границfs[6] вне границfs[7] вне границfs[8] вне границfs[9] вне границ0 10 20 30 40 fs[5] вне границfs[6] вне границfs[7] вне границfs[8] вне границfs[9] вне границГлава 10.
Индексаторы и свойства259Созданный здесь индексатор предотвращает нарушение границ массива. Рассмотрим подробно каждую часть индексатора. Индексатор начинается с такой строки:I public i n t t h i s [ i n t index] {Здесь объявляется индексатор, который оперирует элементами типа i n t . Индекспередается в параметре index. Сам индексатор — открытый для использования любым кодом вне его класса.Теперь приведем код аксессора get.get {if(ok(index)) {errflag = false;return a[index];} else {errflag = true;return 0;Аксессор get предотвращает ошибки нарушения границ.
Если заданный индекснаходится в пределах границ, аксессор get возвращает элемент, соответствующийэтому индексу. А если переданный индекс выходит за пределы границ, операции смассивом не выполняются, но и ничего страшного при этом не происходит. В даннойверсии класса FailSoftArray переменная e r r f l a g содержит результат выполнениякаждой операции. Чтобы узнать, как завершилась очередная операция, достаточнопроанализировать это поле.Ниже приведен код аксессора s e t (). Он также предотвращает ошибки нарушенияграниц.set{if(ok(index)) {a[index] = value;errflag = false;else errflag = true;Если заданный индекс находится в пределах границ, значение, переданное черезпеременную value, присваивается соответствующему элементу массива.
В противномслучае признак ошибки e r r f l a g устанавливается равным значению t r u e . Вспомните,что в любом аксессорном методе value — это автоматически устанавливаемый параметр, который содержит значение, подлежащее записи. Вам не нужно (да вы и несможете) объявлять этот параметр самим.Индексаторы необязательно создавать с поддержкой как get-, так и setаксессоров. Можно создать индексатор, предназначенный только для чтения, реализовав лишь get-аксессор. И точно также можно создать индексатор, предназначенныйтолько для записи, реализовав лишь set-аксессор.Перегрузка индексаторовИндексаторы можно перегружать.
Здесь приведен пример определения классаFailSoftArray, в котором перегружается индексатор для индексов типа double. Вдействительности double-индексатор округляет индекс до ближайшего целого числа.Таким образом, из двух определенных в классе индексаторов будет выполняться тот,для которого окажется наилучшим соответствие типов параметра индексатора и егоаргумента, используемого в качестве индекса.260Часть I. Язык С#// Перегрузка индексатора для класса FailSoftArray.using System;class FailSoftArray {int[] a;// Ссылка на базовый массив.public int Length; // Length (длина) - открытый член.public bool errflag; // Индикатор результата// последней операции.// Создаем массив заданной длины,public FailSoftArray(int size) {a = new int[size];Length = size;// Это int-индексатор для класса FailSoftArray.public int this[int index] {// Это — get-аксессор.get {if(ok(index)) {errflag = false;return a[index];} else {errflag = true;return 0;// Это — set-аксессор.set {if(ok(index)) {a[index] = value;errflag = false;}else errflag = true;/* Это еще один индексатор для класса FailSoftArray.Здесь в качестве индекса принимается double-аргумент.Затем аргумент округляется до ближайшего целогоиндекса.
*/public int this[double idx] {// Это — get-аксессор.get {int index;// Округление до ближайшего целого int-значения.if( (idx - (int) idx) < 0.5) index = (int) idx;else index = (int) idx + 1;if(ok(index) ) {errflag = false;return a[index];} else {Глава 10. Индексаторы и свойства'261errflag = truerreturn 0;// Это — set-аксессор.set {i n t index;// Округление до ближайшего целого int-значения.if( (idx - (int) idx) < 0.5) index « (int) idx;else index = (int) idx + 1;if(ok(index)) {a[index] = value;errflag = false;}else errflag = true;// Метод возвращает 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 >= 0 & index < Length) r e t u r n t r u e ;return false;// Демонстрируем отказоустойчивый массив,c l a s s FSDemo {p u b l i c s t a t i c void Main() {FailSoftArray fs = new FailSoftArray(5);// Помещаем в массив fs несколько значений,f o r ( i n t i=0; i < fs.Length; i++)fs[i] = i;// Теперь используем в качестве индекса// i n t - и double-значения.Console.WriteLine("fs[l]: " + f s [ l ] ) ;Console.WriteLine("fs[2]: " + f s [ 2 ] ) ;Console.WriteLine("fs[l.1]:Console.WriteLine("fs[1.6]:" + fs[l.l]);" + fs[1.6]);Эта программа генерирует такие результаты:fs[l]: 1fs[2]: 2fs[l.l]: 1fs [1.6] : 2Как подтверждают результаты выполнения этой программы, double-индексы округляются до ближайших целых значений.
В частности, число 1.1 округляется до 1, ачисло 1. 6 — до 2.262Часть I. Язык С#Несмотря на то что перегрузка индексатора, показанная в этой программе, вполнедопустима, этот пример — нетипичен. Чаще всего индексатор перегружается, чтобыиметь возможность использовать объект класса в качестве индекса, значение котороговычисляется специальным образом.Индексаторам не требуется базовый массивИндексатор может не использовать базовый массив.