Чтение онлайн

ЖАНРЫ

Интернет-журнал "Домашняя лаборатория", 2007 №9
Шрифт:

Приведу в качестве примера многострадальную процедуру печати объектов, многократные варианты которой уже были рассмотрены. На этот раз формальный аргумент процедуры будет иметь тип Object — прародителя всех классов. Разберем, как можно выяснить, что в процедуру передается массив, как определить его тип и работать с ним уже как с массивом, а не как с переменной класса Object. Вот текст этой процедуры, названной PrintObject;

public static void PrintObj (object A)

{

Console.WriteLine("A.GetType={0})", A.GetType);

if (A.GetType==typeof(System.Int32[]))

{

int[] temp;

temp = (int[])A;

for(int i = 0; i<temp.GetLength(0);i++)

Console.Write("\t temp[{0}]={1}", i,temp[i]);

Console.WriteLine ;

}

else

Console.WriteLine("Аргумент

не является массивом целых");

}//PrintObject

Несколько замечаний к реализации.

Метод GetType, примененный к аргументу, возвращает не тип Object, а реальный тип фактического аргумента. Поэтому можно проанализировать, какому классу принадлежит объект, переданный в процедуру.

На каждой ветви разбора можно создать временный объект нужного типа и скопировать в него переданный аргумент. В данном примере рассматривается только одна ветвь, в которой создается целочисленный одномерный массив temp.

Заметьте, при присваивании значения переменной temp выполняется явное преобразование из класса Object в класс int[].

При наличии переменной temp, выполнение нужных действий над массивом не представляет никаких трудностей.

Приведу два примера вызова этой процедуры:

//работа с процедурой PrintObject

//Корректный и некорректный вызовы

Arrs.PrintObj (col1);

Arrs.PrintObj (соl3);

Вот какой вывод порождается этим фрагментом кода:

Рис. 12.4. Результаты работы процедуры PrintObj

Массивы объектов

Во всех рассмотренных примерах этой главы нам встречались массивы, элементы которых имели только простые значимые типы. В реальных программах массивы объектов и других ссылочных типов встречаются не менее часто. Каков бы ни был тип элементов, большой разницы при работе с массивами нет. Но один важный нюанс все же есть, и его стоит отметить. Он связан с инициализацией элементов по умолчанию. Уже говорилось о том, что компилятор не следит за инициализацией элементов массива и доверяет инициализации, выполненной конструктором массива по умолчанию. Но для массивов ссылочного типа инициализация по умолчанию присваивает ссылкам значение Null. Это означает, что создаются только ссылки, но не сами объекты. По этой причине, пока не будет проведена настоящая инициализация с созданием объектов и заданием ссылок на конкретные объекты, работать с массивом ссылочного типа будет невозможно.

Рассмотрим детали этой проблемы на примере. Определим достаточно простой и интуитивно понятный класс, названный Winners, свойства которого задают имя победителя и его премию, а методы позволяют установить размер премии для каждого победителя и распечатать его свойства. Приведу код, описывающий этот класс:

/// <summary>

/// Класс победителей с именем и

премией

/// </summary>

public class Winners

{

//поля класса

string name;

int price;

//статическое или динамическое поле rnd?

//static Random rnd = new Random;

Random rnd = new Random;

// динамические методы

public void SetVals(string name)

{

this.name = name;

this.price = rnd.Next(5,10)* 1000;

}//SetVals

public void PrintWinner(Winners win)

{

Console.WriteLine("Имя победителя: {0}," +

" его премия — {1}", win.name, win.price);

}//PrintWinner

}//class Winners

Коротко прокомментирую этот текст.

1. Свойство name описывает имя победителя, а свойство price — величину его премии.

2. Свойство rnd необходимо при работе со случайными числами.

3. Метод SetVals выполняет инициализацию. Он присваивает полю name значение, переданное в качестве аргумента, и полю price — случайное значение.

4. Метод PrintWinner — метод печати свойств класса. Без подобного метода не обходится ни один класс.

5. В классе появится еще один статический метод InitAr, но о нем скажу чуть позже.

Пусть теперь в одном из методов нашего тестирующего класса Testing предполагается работа с классом Winners, начинающаяся с описания победителей. Естественно, задается массив, элементы которого имеют тип winners. Приведу начало тестирующего метода, в котором дано соответствующее объявление:

public void TestWinners

{

//массивы объектов

int nwin = 3;

Winners [] wins = new Winners[nwin];

string[] winames = {"Т. Xoap", "H. Вирт", "Э. Дейкстра"};

В результате создан массив wins, состоящий из объектов класса Winners. Что произойдет, если попытаться задать значения полей объектов, вызвав специально созданный для этих целей метод SetVals? Рассмотрим фрагмент кода, осуществляющий этот вызов:

//создание значений элементов массива

for (int i=0; i < wins.Length; i + +)

wins[i].SetVals(winames[i]);

На этапе выполнения будет сгенерировано исключение — нулевая ссылка. Причина понятна: хотя массив wins и создан, но это массив ссылок, имеющих значение null. Сами объекты, на которые должны указывать ссылки, не создаются в момент объявления массива ссылочного типа. Их нужно создавать явно. Ситуация аналогична объявлению массива массивов. И там необходим явный вызов конструктора для создания каждого массива на внутреннем уровне.

Как же создавать эти объекты? Конечно, можно возложить эту обязанность на пользователя, объявившего массив wins, — пусть он и создаст экземпляры для каждого элемента массива. Правильнее все-таки иметь в классе соответствующий метод. Метод должен быть статическим, чтобы его можно было вызывать еще до того, как созданы экземпляры класса, поскольку метод предназначен для создания этих самых экземпляров. Так в нашем классе появился статический метод InitAr;

Поделиться с друзьями: