Читать в оригинале

<< ПредыдущаяОглавлениеСледующая >>


7.5. Класс Array

Нельзя понять многие детали работы с массивами в C#, если не знать устройство класса Array из библиотеки FCL, потомками которого являются все классы-массивы.

Рассмотрим следующие объявления:

//Класс Array
int[ ] ar1 = new int[5];
double[ ] ar2 ={5.5, 6.6, 7.7};
int[ , ] ar3 = new Int32[3,4];

У всех классов, являющихся массивами, много общего, поскольку все они являются потомками класса System.Array. Класс System.Array наследует ряд интерфейсов: ICloneable, IList, ICollection, Enumerable, и обязан реализовать все их методы и свойства. Помимо наследования свойств и методов класса Object и вышеперечисленных интерфейсов, класс Array имеет довольно большое число собственных методов и свойств. Взгляните, как выглядит отношение наследования на семействе классов, определяющих массивы (рис. 24).

Благодаря такому мощному родителю (класс Object), над массивами определены самые разнообразные операции - копирование, поиск, обращение, сортировка, получение различных характеристик. Массивы можно рассматривать как коллекции и устраивать циклы ForEach для перебора всех элементов. Важно и то, что когда у семейства классов есть общий родитель, то можно иметь общие процедуры обработки различных потомков этого родителя. Для общих процедур работы с массивами характерно, что один или несколько формальных аргументов имеют родительский тип Array. Естественно, внутри такой процедуры может понадобиться анализ - какой реальный тип массива передан в процедуру.

Рисунок 24. Отношение наследования на классах-массивах

Рассмотрим пример подобной процедуры. Ранее для печати элементов массива использовались различные процедуры PrintAr1, PrintAr2 и так далее, по одной для каждого класса массива. Теперь приведем общую процедуру, формальный аргумент которой будет принадлежать родителю всех классов- массивов, что позволит передавать массив любого класса в качестве фактического аргумента:

public void PrintAr(string name, Array A) {
          Console.WriteLine(name);
          switch (A.Rank) {
          case 1:
            for (int i = 0; i < A.GetLength(0); i++)
            Console.Write("\t" + name + "[{0}]={1}", i, A.GetValue(i));
            Console.WriteLine();
            break;
          case 2:
            for (int i = 0; i < A.GetLength(0); i++) {
                        for (int j = 0; j < A.GetLength(1); j++)
                                   Console.Write("\t" + name + "[{0},{1}]={2}", i, j, A.GetValue(i, j));
                        Console.WriteLine();
            }
            break;
          default: break;
          }
} //PrintAr

Вот как выглядит создание массивов и вызов процедуры печати:

public void CreateTwoDimAr(int[,] A) {
          for (int i = 0; i < A.GetLength(0); i++)
            for (int j = 0; j < A.GetLength(1); j++ )
                        A[i,j] = rnd.Next(1, 100);
} //CreateTwoDimAr
public void TestCommonPrint() {
          //Класс Array
          int[] ar1 = new int[5];
          double[] ar2 ={ 5.5, 6.6, 7.7 };
          int[,] ar3 = new Int32[3, 4];
          Arrs.CreateOneDimAr(ar1);
          Arrs.PrintAr("ar1", ar1);
          Arrs.PrintAr("ar2", ar2);
          Arrs.CreateTwoDimAr(ar3);
          Arrs.PrintAr("ar3", ar3);
} //TestCommonPrint

Вот результаты вывода массивов ar1, ar2 и ar3 (рис. 25).

Рисунок 25. Печать массивов. Результаты работы процедуры PrintAr

Приведем некоторые комментарии.

Первое, на что следует обратить внимание: формальный аргумент процедуры принадлежит базовому классу Array, наследниками которого являются все массивы в CLR и, естественно, все массивы C#.

Для того чтобы сохранить возможность работы с индексами, как в одномерном, так и в двумерном случае, пришлось организовать разбор случаев. Свойство Rank, возвращающее размерность массива, используется в этом разборе.

К элементам массива A, имеющего класс Array, нет возможности прямого доступа в обычной манере - A [<индексы>], но зато есть специальные методы GetValue (<индексы>) и SetValue (<индексы>).

Естественно, разбор случаев можно продолжить, придав процедуре большую функциональность.

Отметим, если разбор случаев вообще не делать, а использовать PrintAr только для печати одномерных массивов, то она будет столь же проста, как и процедура PrintAr1, но сможет печатать любые одномерные массивы, независимо от типа их элементов.

Свойства класса Array

Основные свойства класса Array приведены в таблице 3. При работе с массивом его можно закрыть на время обработки (для чего используется соответствующее свойство SyncRoot), что запрещает его модификацию каким-либо потоком:

Array myCol = new int[ ];
lock( myCol.SyncRoot ) {
          foreach ( Object item in myCol )
          {
            // безопасная обработка массива
          }
}

Остальные представленные свойства не требуют особых комментариев.

Таблица 3. Свойства класса Array

Свойство

Родитель

Описание

IsFixedSize

Интерфейс IList

True, если массив статический

IsReadOnly

Интерфейс IList

Для всех массивов имеет значение false

IsSynchronized

Интерфейс ICollection

True или False, в зависимости от того, установлена ли синхронизация доступа для массива

SyncRoot

Интерфейс ICollection

Собственный метод синхронизации доступа к массиву.

Length

 

Число элементов массива

Rank

 

Размерность массива

Статические методы класса Array

Статические методы класса Array позволяют решать самые разнообразные задачи:

Copy - позволяет копировать весь массив или его часть в другой массив.

IndexOf, LastIndexOf - определяют индексы первого и последнего вхождения образца в массив, возвращая -1, если такового вхождения не обнаружено.

Reverse - выполняет обращение одномерного массива, переставляя элементы в обратном порядке.

Sort - осуществляет сортировку массива.

BinarySearch - определяет индекс первого вхождения образца в отсортированный массив, используя алгоритм двоичного поиска.

Clear - выполняет начальную инициализацию элементов. В зависимости от типа элементов устанавливает значение 0 для арифметического типа, false - для логического типа, Null для ссылок, "" - для строк.

CreateInstance - создание экземпляров класса.

Класс Array, в отличие от многих классов, может создавать свои экземпляры не только с помощью конструктора new, но и при вызове метода CreateInstance:

Array my2Dar = Array.CreateInstance(typeof(double), 2,2)

Все методы перегружены и имеют ряд модификаций. Большинство из

этих методов применимо только к одномерным массивам.

Динамические методы класса Array

Динамические методы класса Array представлены таблицей 4.

Таблица 4. Динамические методы класса Array

Метод

Родитель

Описание

Clone

Интерфейс ICloneable

Позволяет создать плоскую или глубокую копию массива. В первом случае создаются только элементы первого уровня, а ссылки указывают на те же самые объекты. Во втором случае копируются объекты на всех уровнях. Для массивов создается только плоская копия.

CopyTo

Интерфейс ICollection

Копируются все элементы одномерного массива в другой одномерный массив, начиная с заданного индекса: col1.CopyTo(col2,0);

GetEnumerator

Интерфейс Enumerable

Реализует цикл ForEach

GetLength

 

Возвращает число элементов массива по указанному измерению.

GetLowerBound, GetUpperBound

 

Возвращает нижнюю и верхнюю границу по указанному измерению. Для массивов нижняя граница всегда равна нулю.

GetValue,

 

Возвращает или устанавливает значение элемента массива с указанными индексами.

SetValue

 

 

Initialize

 

Инициализирует элементы, вызывая соответствующий конструктор. Может быть применен только к массивам значимого типа. Как правило, не используется в обычных программах.

 



<< ПредыдущаяОглавлениеСледующая >>