Класс Array
Нельзя понять многие детали работы с массивами в C#, если не знать устройство класса Array из библиотеки FCL, потомками которого являются все классы-массивы. Рассмотрим следующие объявления:
//Класс Array int[] ar1 = new int[5]; double[] ar2 ={5.5, 6.6, 7.7}; int[,] ar3 = new Int32[3,4];
Зададимся естественным вопросом: к какому или к каким классам принадлежат объекты ar1, ar2 и ar3? Ответ прост: все они принадлежат к разным классам. Переменная ar1 принадлежит к классу int[] - одномерному массиву значений типа int, ar2 - double[] - одномерному массиву значений типа double, ar3 - двумерному массиву значений типа int. Следующий закономерный вопрос: а что общего есть у этих трех объектов? Прежде всего, все три класса этих объектов, как и другие классы, являются потомками класса Object, а потому имеют общие методы, наследованные от класса Object и доступные объектам этих классов.
У всех классов, являющихся массивами, много общего, поскольку все они являются потомками класса System.Array. Класс System.Array наследует ряд интерфейсов: ICloneable, IList, ICollection, IEnumerable, а, следовательно, обязан реализовать все их методы и свойства. Помимо наследования свойств и методов класса Object и вышеперечисленных интерфейсов, класс Array имеет довольно большое число собственных методов и свойств. Взгляните, как выглядит отношение наследования на семействе классов, определяющих массивы.
Рис. 12.1. Отношение наследования на классах-массивах
Благодаря такому мощному родителю, над массивами определены самые разнообразные операции - копирование, поиск, обращение, сортировка, получение различных характеристик. Массивы можно рассматривать как коллекции и устраивать циклы For Each для перебора всех элементов. Важно и то, что когда у семейства классов есть общий родитель, то можно иметь общие процедуры обработки различных потомков этого родителя. Для общих процедур работы с массивами характерно, что один или несколько формальных аргументов имеют родительский тип Array. Естественно, внутри такой процедуры может понадобиться анализ - какой реальный тип массива передан в процедуру.
Рассмотрим пример подобной процедуры. Ранее я для печати элементов массива использовал различные процедуры PrintAr1, PrintAr2 и так далее, по одной для каждого класса массива. Теперь я приведу общую процедуру, формальный аргумент которой будет принадлежать родителю всех классов-массивов, что позволит передавать массив любого класса в качестве фактического аргумента:
public static 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