1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 48
Текст из файла (страница 48)
При использовании индексатора аксессорывызываются автоматически, и в качестве параметра оба аксессора принимают индекс.Если индексатор стоит слева от оператора присваивания, вызывается аксессор set иустанавливается элемент, заданный параметром индекс. В противном случае вызываетсяаксессор get и возвращается значение, соответствующее параметру индекc.Глава 10. Индексаторы и свойства257Метод set также получает значение (именуемое value), которое присваивается элементумассива, найденному по заданному индексу.Одно из достоинств индексатора состоит в том, что он позволяет точно управлятьхарактером доступа к массиву, “отбраковывая” попытки некорректного доступа.Рассмотрим пример.
В следующей программе класс FailSoftArray реализует массив,который “вылавливает” ошибки нарушения границ, предотвращая возникновениеисключительных ситуаций. Это достигается за счет инкапсуляции массива как закрытогочлена класса и осуществления доступа к этому массиву только через индексатор. При такомподходе можно предотвратить любую попытку получить доступ к массиву за пределами егограниц, причем последствия попытки нарушить границы в этом случае можно сравнить с“мягкой посадкой”, а не с “катастрофическим падением”. Поскольку в классеFailSoftArray используется индексатор, к массиву можно обращаться с помощьюобычной формы записи ([]).// Использование индексатора для создания// отказоустойчивого массива.using System;class 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;}elseerrflag = true;}}// Метод возвращает значение true, если258Часть I.
Язык C#// индекс - в пределах границ.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); i++)fs[i] = i*10;for(int i=0; i < (fs.Length * 2); i++) {x = fs[i];if(x != -1) Console.Write(x + " ");}Console.WriteLine();// Теперь генерируем некорректный доступ.Console.WriteLine("\nРабота с уведомлением об ошибках.");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); i++) {x = fs[i];if(!fs.errflag)Console.Write(x + " ");elseConsole.WriteLine("fs[" + i + "] вне границ");}}}При выполнении этой программы получаем такие результаты:"Мягкое приземление".0 10 20 30 40 0 0 0 0 0Работа с уведомлением об ошибках.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Созданный здесь индексатор предотвращает нарушение границ массива. Рассмотримподробно каждую часть индексатора. Индексатор начинается с такой строки:public int this[int index] {Здесь объявляется индексатор, который оперирует элементами типа int. Индекспередается в параметре index.
Сам индексатор — открытый для использования любымкодом вне его класса.Теперь приведем код аксессора get.get {if(ok(index)) {errflag = false;return a[index];} else {errflag = true;return 0;}}Аксессор get предотвращает ошибки нарушения границ. Если заданный индекснаходится в пределах границ, аксессор get возвращает элемент, соответствующий этомуиндексу. А если переданный индекс выходит за пределы границ, операции с массивом невыполняются, но и ничего страшного при этом не происходит.
В данной версии классаFailSoftArray переменная errflag содержит результат выполнения каждой операции.Чтобы узнать, как завершилась очередная операция, достаточно проанализировать это поле.Ниже приведен код аксессора set(). Он также предотвращает ошибки нарушенияграниц.set {if(ok(index)) {a[index] = value;errflag = false;}else errflag = true;}Если заданный индекс находится в пределах границ, значение, переданное черезпеременную value, присваивается соответствующему элементу массива.
В противномслучае признак ошибки errflag устанавливается равным значению true. Вспомните, чтов любом аксессорном методе value — это автоматически устанавливаемый параметр,который содержит значение, подлежащее записи. Вам не нужно (да вы и не сможете)объявлять этот параметр самим.Индексаторы необязательно создавать с поддержкой как get-, так и set-аксессоров.Можно создать индексатор, предназначенный только для чтения, реализовав лишь getаксессор. И точно также можно создать индексатор, предназначенный только для записи,реализовав лишь set-аксессор.Перегрузка индексаторовИндексаторы можно перегружать. Здесь приведен пример определения классаFailSoftArray, в котором перегружается индексатор для индексов типа double.
Вдействительности double-индексатор округляет индекс до ближайшего целого числа.Таким образом, из двух определенных в классе индексаторов будет выполняться тот, длякоторого окажется наилучшим соответствие типов параметра индексатора и его аргумента,используемого в качестве индекса.260Часть I. Язык C#// Перегрузка индексатора для класса 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;elseindex = (int) idx + 1;if(ok(index)) {errflag = false;return a[index];} else {Глава 10. Индексаторы и свойства261}}errflag = true;return 0;// Это — set-аксессор.set {int index;// Округление до ближайшего целого int-значения.if( (idx - (int) idx) < 0.5)index = (int) idx;elseindex = (int) idx + 1;if(ok(index)) {a[index] = value;errflag = false;}elseerrflag = true;}}// Метод возвращает true, если индекс внутри границ.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);// Помещаем в массив fs несколько значений.for(int i=0; i < fs.Length; i++)fs[i] = i;}}// Теперь используем в качестве индекса// int- и double-значения.Console.WriteLine("fs[1]: " + fs[1]);Console.WriteLine("fs[2]: " + fs[2]);Console.WriteLine("fs[1.1]: " + fs[1.1]);Console.WriteLine("fs[1.6]: " + fs[1.6]);Эта программа генерирует такие результаты:fs[1]: 1fs[2]: 2fs[1.1]: 1fs[1.6]: 2Как подтверждают результаты выполнения этой программы, double-индексыокругляются до ближайших целых значений.
В частности, число 1.1 округляется до 1, ачисло 1.6 — до 2.262Часть I. Язык C#Несмотря на то что перегрузка индексатора, показанная в этой программе, вполнедопустима, этот пример — нетипичен. Чаще всего индексатор перегружается, чтобы иметьвозможность использовать объект класса в качестве индекса, значение котороговычисляется специальным образом.Индексаторам не требуется базовый массивИндексатор может не использовать базовый массив. Вполне достаточно, чтобыиндексатор обеспечивал функционирование, которое для пользователя выглядело бы, както, что обеспечивают массивы. Например, следующая программа включает индексатор,который действует подобно массиву, предназначенному только для чтения.
Этот “массив”содержит степени числа 2 для чисел от 0 до 15. Однако в действительности никакогомассива не существует. Вместо этого индексатор просто (и быстро) вычисляетсоответствующее значение для заданного индекса.// Индексаторы не обязательно должны использовать// реальные массивы.using System;class PwrOfTwo {/* Доступ к логическому массиву, который содержитстепени числа 2 для чисел от 0 до 15. */public int this[int index] {// Вычисляем и возвращаем степень числа 2.get {if((index >= 0) && (index < 16))return pwr(index);else return -1;}// Здесь нет set-аксессора.}int pwr(int p) {int result = 1;for(int i=0; i<p; i++)result *= 2;return result;}}class UsePwrOfTwo {public static void Main() {PwrOfTwo pwr = new PwrOfTwo();Console.Write("Первые 8 степеней числа 2: ");for(int i=0; i < 8; i++)Console.Write(pwr[i] + " ");Console.WriteLine();Console.Write("А вот несколько ошибок: ");Console.Write(pwr[-1] + " " + pwr[17]);}}Console.WriteLine();Глава 10.
Индексаторы и свойства263Вот результаты выполнения этой программы:Первые 8 степеней числа 2: 12 4 8 16 32 64 128А вот несколько ошибок: -1 -1Обратите внимание на то, что индексатор класса UsePwrOfTwo включает getаксессор, но обходится без set-аксессора. Это означает, что индексатор предназначентолько для чтения. Таким образом, объект класса UsePwrOfTwo можно использовать вправой части инструкции присвоения, но ни в коем случае не в левой. Например, попыткадобавить эту инструкцию в предыдущую программу, обречена на неудачу:pwr[0] = 11; // не скомпилируетсяЭта инструкция вызовет ошибку компиляции, поскольку в индексаторе не определенset-аксессор.На использование индексаторов накладывается два ограничения.