- •10 Индексаторы и свойства
- •10.1 Индексаторы
- •10.1.1 Создание одномерных индексаторов
- •10.1.2 Перегрузка индексаторов
- •10.1.3 Индексаторы без базового массива
- •10.1.4 Многомерные индексаторы
- •10.2 Свойства
- •10.2.1 Автоматически реализуемые свойства
- •10.2.2 Применение инициализаторов объектов в свойствах
- •10.2.3 Ограничения, присущие свойствам
- •10.3 Применение модификаторов доступа в аксессорах
- •10.4 Применение индексаторов и свойств
10.4 Применение индексаторов и свойств
В предыдущих примерах программ был продемонстрирован основной принцип действия индексаторов и свойств, но их возможности не были раскрыты в полную силу. Поэтому в завершение этой главы обратимся к примеру класса RangeArray, в котором индексаторы и свойства используются для создания типа массива с пределами индексирования, определяемыми пользователем.
Как вам должно быть уже известно, индексирование всех массивов в C# начинается с нуля. Но в некоторых приложениях индексирование массива удобнее начинать с любой произвольной точки отсчета: с 1 или даже с отрицательного числа, например от -5 и до 5. Рассматриваемый здесь класс RangeArray разработан таким образом, чтобы допускать подобного рода индексирование массивов.
Используя класс RangeArray, можно написать следующий фрагмент кода.
RangeArray rа = new RangeArray(-5, 10); // массив с индексами от
// -5 до 10
for(int i=-5; i <= 10; i++) ra[i] = i; // индексирование массива
// от -5 до 10
Нетрудно догадаться, что в первой строке этого кода конструируется объект класса RangeArray с пределами индексирования массива от -5 до 10 включительно. Первый аргумент обозначает начальный индекс, а второй конечный индекс. Как только объект га будет сконструирован, он может быть проиндексирован как массив в пределах от -5 до 10.
Ниже приведен полностью класс RangeArray вместе с классом RangeArrayDemo, в котором демонстрируется индексирование массива в заданных пределах. Класс RangeArray реализован таким образом, чтобы поддерживать массивы типа int, но при желании вы можете изменить этот тип на любой другой.
Листинг 10.11
/* Создать класс со специально указываемыми пределами
Индексирования массива. Класс RangeArray допускает
индексирование массива с любого значения, а не только
с нуля. При создании объекта класса RangeArray
указываются начальный и конечный индексы. Допускается
также указывать отрицательные индексы. Например, можно
создать массивы, индексируемые от -5 до 5, от 1 до 10
или же от 50 до 56. */
using System;
class RangeArray
{
// Закрытые данные
int[] a; // ссылка на базовый массив
int lowerBound; // наименьший индекс
int upperBound; // наибольший индекс
// An auto-implemented, read-only Length property.
public int Length { get; private set; }
// Автоматически реализуемое и доступное только для чтения
// свойство Lenght
public bool Error { get; private set; }
// Построить массив по заданному размеру
public RangeArray(int low, int high)
{
high++;
if(high <= low) {
Console.WriteLine("Неверные индексы");
high = 1; // создать для надежности минимально допустимый
// массив
low = 0;
}
a = new int[high - low];
Length = high - low;
lowerBound = low;
upperBound = --high;
}
// Это индексатор для класса RangeArray.
public int this[int index]
{
// Это аксессор get
get
{
if(ok(index))
{
Error = false;
return a[index - lowerBound];
}
else
{
Error = true;
return 0;
}
}
// Это аксессор set
set
{
if(ok(index))
{
a[index - lowerBound] = value;
Error = false;
}
else Error = true;
}
}
// Возвратить true, если индекс находится
// в установленных границах
private bool ok(int index) {
if(index >= lowerBound & index <= upperBound) return true;
return false;
}
}
// Продемонстрировать применение массива с произвольно
// задаваемыми пределами индексирования
class RangeArrayDemo
{
static void Main()
{
RangeArray ra = new RangeArray(-5, 5);
RangeArray ra2 = new RangeArray(1, 10);
RangeArray ra3 = new RangeArray(-20, -12);
// Использовать объект ra в качестве массива
Console.WriteLine("Длина массива ra: " + ra.Length);
for(int i = -5; i <= 5; i++)
ra[i] = i;
Console.Write("Содержимое массива ra: ");
for(int i = -5; i <= 5; i++)
Console.Write(ra[i] + " ");
Console.WriteLine("\n");
// Использовать объект ra2 в качестве массива
Console.WriteLine("Длина массива ra2: " + ra2.Length);
for(int i = 1; i <= 10; i++)
ra2[i] = i;
Console.Write("Содержимое массива ra2: ");
for(int i = 1; i <= 10; i++)
Console.Write(ra2[i] + " ");
Console.WriteLine("\n");
// Использовать объект ra3 в качестве массива
Console.WriteLine("Длина массива ra3: " + ra3.Length);
for(int i = -20; i <= -12; i++)
ra3[i] = i;
Console.Write("Содержимое массива ra3: ");
for(int i = -20; i <= -12; i++)
Console.Write(ra3[i] + " ");
Console.WriteLine("\n");
}
}
При выполнении этого кода получается следующий результат.
Длина массива rа: 11
Содержимое массива rа: -5 -4 -3 -2 -1 0 1 2 3 4 5
Длина массива rа2: 10
Содержимое массива rа2: 1 2 3 4 5 6 7 8 9 10
Длина массива rа3: 9
Содержимое массива ra3: -20 -19 -18 -17 -16 -15 -14 -13 -12
Как следует из результата выполнения приведенного выше кода, объекты типа RangeArray можно индексировать в качестве массивов, начиная с любой точки отсчета, а не только с нуля. Рассмотрим подробнее саму реализацию класса RangeArray.
В начале класса RangeArray объявляются следующие закрытые переменные экземпляра.
// Закрытые данные.
int[] а; // ссылка на базовый массив
int lowerBound // наименьший индекс
int upperBound // наибольший индекс
Переменная а служит для обращения к базовому массиву по ссылке. Память для него распределяется конструктором класса RangeArray. Нижняя граница индексирования массива хранится в переменной lowerBound, а верхняя граница — в переменной upperBound.
Далее объявляются автоматически реализуемые свойства Length и Error.
// Автоматически реализуемое и доступное только
// для чтения свойство Length.
public int Length { get; private set; }
// Автоматически реализуемое и доступное только
// для чтения свойство Error.
public bool Error { get; private set; }
Обратите внимание на то, что в обоих свойства аксессор set обозначен как private. Как пояснялось выше, такое объявление автоматически реализуемого свойства, по существу, делает его доступным только для чтения.
Ниже приведен конструктор класса RangeArray.
Листинг 10.12
// Построить массив по заданному размеру
public RangeArray(int low, int high)
{
high++;
if(high <= low)
{
Console.WriteLine("Неверные индексы");
high = 1; // создать для надежности минимально
// допустимый массив
low = 0;
}
а = new int[high - low];
Length = high - low;
lowerBound = low;
upperBound = —high;
}
При конструировании объекту класса RangeArray передается нижняя граница массива в качестве параметра low, а верхняя граница — в качестве параметра high. Затем значение параметра high инкрементируется, поскольку пределы индексирования массива изменяются от low до high включительно. Далее выполняется следующая проверка: является ли верхний индекс больше нижнего индекса. Если это не так, то выдается сообщение об ошибке и создается массив, состоящий из одного элемента. После этого для массива распределяется память, а ссылка на него присваивается переменной а. Затем свойство Length устанавливается равным числу элементов массива. И наконец, устанавливаются переменные lowerBound и upperBound.
Далее в классе RangeArray реализуется его индексатор, как показано ниже.
Листинг 10.13
// Это индексатор для класса RangeArray
public int this[int index]
{
// Это аксессор get
get
{
Error = false;
return a[index - lowerBound];
}
else
{
Error = true;
return 0;
}
// Это аксессор set
set
{
if(ok(index))
{
a[index - lowerBound] = value;
Error = false;
}
else Error = true;
}
}
Этот индексатор подобен тому, что использовался в классе FailSoftArray, за одним существенным исключением. Обратите внимание на следующее выражение, в котором индексируется массив а.
index - lowerBound
В этом выражении индекс, передаваемый в качестве параметра index, преобразуется в индекс с отсчетом от нуля, пригодный для индексирования массива а. Данное выражение действует при любом значении переменной lowerBound: положительном, отрицательном или нулевом.
Ниже приведен метод ok().
Листинг 10.14
// Возвратить логическое значение true, если
// индекс находится в установленных границах
private bool ok(int index)
{
if(index >= lowerBound & index <= upperBound) return true;
return false;
}
Этот метод аналогичен использовавшемуся в классе FailSoftArray, за исключением того, что в нем контроль границ массива осуществляется по значениям переменных lowerBound и upperBound.
Класс RangeArray демонстрирует лишь одну разновидность специализированного массива, который может быть создан с помощью индексаторов и свойств. Существуют, конечно, и другие. Аналогичным образом можно, например, создать динамические массивы, которые расширяются или сужаются по мере надобности, ассоциативные и разреженные массивы. Попробуйте создать один из таких массивов в качестве упражнения.
