1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 49
Текст из файла (страница 49)
Во-первых,поскольку в индексаторе не определяется область памяти, получаемое индексаторомзначение нельзя передавать методу в качестве ref- или out-параметра. Во-вторых,индексатор должен быть членом экземпляра своего класса, поэтому его нельзя объявлять сиспользованием ключевого слова static.Многомерные индексаторыМожно создавать индексаторы и для многомерных массивов.
Например, вот какработает двумерный отказоустойчивый массив. Обратите особое внимание на способобъявления индексатора в этом классе.// Двумерный отказоустойчивый массив.using System;class FailSoftArray2D {int[,] a; // Ссылка на базовый двумерный массив.int rows, cols; // размерностиpublic int Length; // Length - открытый член.public bool errflag; // Индикатор результата// последней операции.// Создаем массив заданного размера.public FailSoftArray2D(int r, int с) {rows = r;cols = с;a = new int[rows, cols];Length = rows * cols;}// Это индексатор для класса FailSoftArray2D.public int this[int index1, int index2] {// Это — get-аксессор.get {if(ok(index1, index2)) {errflag = false;return a[index1, index2];} else {errflag = true;return 0;}}264Часть I. Язык C#// Это — set-аксессор.set {if(ok(index1, index2)) {a[index1, index2] = value;errflag = false;} elseerrflag = true;}}// Метод возвращает значение true, если индексы// находятся внутри границ.private bool ok(int index1, int index2) {if( index1 >= 0 & index1 < rows &index2 >= 0 & index2 < cols)return true;return false;}}// Демонстрируем использование двумерного индексатора.class TwoDIndexerDemo {public static void Main() {FailSoftArray2D fs = new FailSoftArray2D(3, 5);int x;// Демонстрируем "мягкую посадку" при ошибках.Console.WriteLine("Мягкое приземление.");for(int i=0; i < 6; i++)fs[i, i] = i*10;for(int i=0; i < 6; i++) {x = fs[i, i ];if(x != -1) Console.Write(x + " ");}Console.WriteLine();//А теперь генерируем ошибки.Console.WriteLine("\nРабота с уведомлением об ошибках.");for(int i=0; i < 6; i++) {fs[i,i] = i*10;if(fs.errflag)Console.WriteLine("fs[" + i + ", " + i + "] вне границ");}for(int i=0; i < 6; i++) {x = fs[i,i];if(!fs.errflag)Console.Write(x + " ");elseConsole.WriteLine("fs[" + i + ", " + i + "] вне границ");}}}Глава 10.
Индексаторы и свойства265Результаты, генерируемые этой программой:Мягкое приземление. 0 10 20 0 0 0Работа с уведомлением об ошибках.fs[3, 3] вне границfs[4, 4 ] вне границfs[5, 5] вне границ0 10 20 fs[3, 3] вне границfs[4, 4] вне границfs[5, 5] вне границСвойстваСвойство — это второй специальный тип членов класса, о котором мы собиралисьпоговорить в этой главе. Свойство включает поле и методы доступа к этому полю.
Частотребуется создать поле, которое должно быть доступно для пользователей объекта, нопрограммист при этом хочет осуществлять управление операциями, разрешенными квыполнению над этим полем. Например, по некоторым обстоятельствам вы желаетеограничить диапазон значений, которые можно присваивать этому полю. Несмотря на точто этого можно достичь с помощью закрытой переменной и методов доступа к ней,свойство предлагает более удобный и простой способ решения этой задачи.Свойства во многом напоминают индексаторы.
Свойство состоит из имени и парыаксессоров (get и set). Аксессоры используются для чтения содержимого переменной изаписи в нее нового значения. Основное достоинство свойства состоит в том, что его имяможно использовать в выражениях и инструкциях присваивания подобно обычнойпеременной, хотя в действительности здесь будут автоматически вызываться get- и setаксессоры. Автоматический вызов аксессоров и роднит свойства с индексаторами.Формат записи свойства таков:тип имя{get{// код аксессора чтения поля}set{// код аксессора записи поля}Здесь тип — это тип свойства (например, int), а имя — его имя. После определениясвойства любое использование его имени означает вызов соответствующего аксессора.Аксессор set автоматически принимает параметр с именем value, который содержитзначение, присваиваемое свойству.Важно понимать, что свойства не определяют область памяти.
Следовательно,свойство управляет доступом к полю, но самого поля не обеспечивает. Это поле должнобыть задано независимо от свойства.Рассмотрим простой пример, в котором определяется свойство myprop,используемое для доступа к полю prop. Это свойство позволяет присваивать полю толькоположительные числа.// Пример использования свойства.using System;266Часть I. Язык C#class SimpProp {int prop; // Это поле управляется свойством myprop.public SimpProp() {prop =0;}/* Это свойство поддерживает доступ к закрытойпеременной экземпляра prop.
Оно позволяетприсваивать ей только положительные числа. */public int myprop {get {return prop;}set {if(value >= 0)prop = value;}}}// Демонстрируем использование свойства.class PropertyDemo {public static void Main() {SimpProp ob = new SimpProp();Console.WriteLine("Исходное значение ob.myprop: " +ob.myprop);ob.myprop = 100; // Присваиваем значение.Console.WriteLine("Значение ob.myprop: " +ob.myprop);// Переменной prop невозможно присвоить// отрицательное значение.Console.WriteLine("Попытка присвоить -10 свойству ob.myprop");ob.myprop = -10;Console.WriteLine("Значение ob.myprop: " + ob.myprop);}}Результаты выполнения этой программы выглядят так:Исходное значение ob.myprop: 0Значение ob.myprop: 100Попытка присвоить -10 свойству ob.mypropЗначение ob.myprop: 100На этой программе стоит остановиться подробнее.
В классе SimpProp определяетсязакрытое поле prop и свойство myprop, которое управляет доступом к полю prop. Какупоминалось выше, свойство само не определяет область хранения поля, а лишь управляетдоступом к нему. Поэтому без определения базового поля определение свойства теряетвсякий смысл. Более того, поскольку поле prop закрытое, к нему можно получить доступтолько посредством свойства myprop.Свойство myprop определяется как public-член класса, чтобы к нему можно былообратиться с помощью кода вне класса, включающего это свойство. В этом есть своялогика, поскольку свойство предоставляет доступ к закрытому полю prop с помощьюаксессоров: get-аксессор просто возвращает значение prop, a set-аксессор устанавливаетновое значение prop, если оно положительно. Таким образом, свойствоГлава 10.
Индексаторы и свойства267myprop управляет тем, какие значения может содержать поле prop. В этом и состоитважность свойств.Свойство myprop предназначено для чтения и записи, поскольку позволяет какпрочитать содержимое своего базового поля, так и записать в него новое значение. Номожно создавать свойства, предназначенные только для чтения (определив лишь getаксессор) либо только для записи (определив лишь set-аксессор).Мы можем использовать свойство для дальнейшего усовершенствования класса,определяющего отказоустойчивый массив. Как вы уже знаете, с каждым массивом связаносвойство Length. До сих пор в классе FailSoftArray для этой цели простоиспользовалось открытое целочисленное поле Length.
Такое решение — не самое лучшее,поскольку в этом случае можно записать в поле Length значение, отличное от реальнойдлины этого массива. (Например, какой-нибудь программист с дурными наклонностями могумышленно ввести это значение.) Потенциально опасную ситуацию можно исправить,заменив открытую переменную Length свойством, предназначенным только для чтения,как показано в следующей версии класса FailSoftArray.// Добавляем в класс FailSoftArray свойство Length.using System;class FailSoftArray {int[] a; // Ссылка на базовый массив.int len; // Длина массива, основа для свойства Length.public bool errflag; // Индикатор результата// последней операции.// Создаем массив заданного размера.public FailSoftArray(int size) {a = new int[size];len = size;}// Свойство Length предназначено только для чтения.public int Length {get {return len;}}// Это — индексатор класса 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;268Часть I.
Язык C#}}}errflag = false;}else errflag = true;// Метод возвращает true, если индекс внутри границ.private bool ok(int index) {if(index >= 0 & index < Length) return true;return false;}// Демонстрируем улучшенный отказоустойчивый массив.class ImprovedFSDemo {public static void Main() {FailSoftArray fs = new FailSoftArray(5);int x;// Свойство Length можно считывать.for(int i=0; i < fs.Length; i++)fs[i] = i*10;for(int i=0; i < fs.Length; i++) {x = fs[i];if(x != -1) Console.Write(x + " ");}}}Console.WriteLine();// fs.Length = 10; // Ошибка, запись запрещена!Теперь Length — это свойство, которое в качестве области памяти используетзакрытую переменную len.
В этом свойстве определен только get-аксессор, и потомусвойство Length можно только читать, но не изменять. Чтобы убедиться в этом,попробуйте убрать символы комментариев в начале следующей строки программы:// fs.Length = 10; // Ошибка, запись запрещена!При попытке скомпилировать программу с этой строкой кода вы получите сообщениеоб ошибке, уведомляющее о том, что свойство Length предназначено только для чтения.Внесение в класс FailSoftArray свойства Length значительно улучшило его, нона этом рано ставить точку.
Член errflag — еще один кандидат на “превращение” изобычной переменной экземпляра в свойство со всеми преимуществами, поскольку доступ кнему следует ограничить до определения “только для чтения”. Приводим окончательнуюверсию класса FailSoftArray, в которой создано свойство Error, использующее вкачестве области хранения индикатора ошибки исходную переменную errflag.// Превращаем переменную errflag в свойство.using System;class FailSoftArray {int[] a; // Ссылка на базовый массив.int len; // Длина массива.Глава 10. Индексаторы и свойства269bool errflag; // Теперь этот член закрыт.// Создаем массив заданного размера.public FailSoftArray(int size) {a = new int[size];len = size;}// Свойство Length предназначено только для чтения.public int Length {get {return len;}}// Свойство Error предназначено только для чтения.public bool Error {get {return errflag;}}// Это - индексатор класса 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, если индекс внутри границ.private bool ok(int index) {if(index >= 0 & index < Length) return true;return false;}}// Демонстрируем улучшенный отказоустойчивый массив.class FinalFSDemo {public static void Main() {FailSoftArray fs = new FailSoftArray(5);// Используем свойство Error.270Часть I.
Язык C#for(int i=0; i < fs.Length + 1; i++) {fs[i] = i*10;if(fs.Error)}}}Console.WriteLine("Ошибка в индексе " + i);Создание свойства Error заставило нас внести в класс FailSoftArray дваизменения. Во-первых, переменную errflag пришлось сделать закрытой, поскольку онатеперь используется в качестве базовой области памяти для свойства Error. В результате кчлену errflag теперь нельзя обращаться напрямую.