Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C#, 320 стр..doc
Скачиваний:
8
Добавлен:
16.11.2018
Размер:
3.64 Mб
Скачать

Процедуры и массивы

В наших примерах массивы неоднократно передавались процедурам в качестве входных аргументов и возвращались в качестве результатов.

В лекции 9 подробно описывались особенности передачи аргументов в процедуру. Остается подчеркнуть только некоторые детали:

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

  • Когда массив является выходным аргументом процедуры, как аргумент C в процедуре MultMatr, выходной аргумент совсем не обязательно снабжать ключевым словом ref или out (хотя и допустимо). Передача аргумента по значению в таких ситуациях так же хороша, как и передача по ссылке. В результате вычислений меняется сам массив в динамической памяти, а ссылка на него остается постоянной. Процедура и ее вызов без ключевых слов выглядит проще, поэтому обычно они опускаются. Заметьте, в процедуре GetSizes, где определялись границы массива, ключевое слово out, сопровождающее аргументы, совершенно необходимо.

  • Может ли процедура-функция возвращать массив в качестве результата? В C# ответ на этот вопрос положителен. В следующей лекции будет приведен пример подобной функции.

Класс 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

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

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.

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

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

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

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

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

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

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]