Интернет-журнал "Домашняя лаборатория", 2007 №9 - Журнал «Домашняя лаборатория»
Шрифт:
Интервал:
Рассмотрим детали этой проблемы на примере. Определим достаточно простой и интуитивно понятный класс, названный 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;
//статический метод
public static Winners[] InitAr(Winners[] Winar)
{
for (int i=0; i < Winar.Length; i + +)
Winar[i] = new Winners();
return(Winar);
}//InitAr
Методу передается массив объектов, возможно, с нулевыми ссылками. Он возвращает тот же массив, но уже с явно определенными ссылками на реально созданные объекты. Теперь достаточно вызвать этот метод, после чего можно спокойно вызывать и метод SetVals. Вот как выглядит правильная последовательность вызовов методов класса Winners;
Winners.InitAr(wins);
//создание значений элементов массива
for(int i=0; i < wins.Length; i++)
wins[i].SetVals(winames[i]);
//печать значений элементов массива
for (int i=0; i < wins.Length; i + +)
wins[i].PrintWinner(wins[i]);
}//TestWinners
Теперь все корректно, массивы создаются, элементы заполняются нужными значениями, их можно распечатать:
Рис. 12.5. Печать элементов массива wins
Обратите внимание, что всем победителям назначена одна и та же премия. Хотя понятно, что дело в программной ошибке, но в ней можно видеть и знак свыше. Коль скоро для победителей выбраны такие имена, почитаемые всеми программистами, то негоже пытаться расставить их по ранжиру даже в примере.
Что же касается ошибки, то она связана с тем, что в данном случае свойство rnd следует сделать статическим, чтобы оно было одно на все экземпляры класса. В тексте описания варианта класса приведены оба варианта объявления свойства, один из которых закомментирован.
Массивы. Семантика присваивания
Преобразования между классами массивов и родительскими классами Array и Object уже рассматривались. А существуют ли другие преобразования между классами массивов? Что происходит при присваивании х=е; (передаче аргументов в процедуру), если х и е — это массивы разных классов? Возможно ли присваивание? Ответ на этот вопрос положительный, хотя накладываются довольно жесткие ограничения на условия, когда такие преобразования допустимы. Известно, например, что между классами Int и Object существуют взаимные преобразования — в одну сторону явное, в другую неявное. А вот между классами Int [] и Object [] нет ни явных, ни неявных преобразований. С другой стороны, такое преобразование существует между классами String [] и Object []. в чем же тут дело, и где логика? Запомните, главное ограничение на возможность таких преобразований состоит в том, что элементы массивов должны иметь ссылочный тип. А теперь притянем сюда логику. Крайне желательно обеспечить возможность проведения преобразований между массивами, элементы которых принадлежат одному семейству классов, связанных отношением наследования. Такая возможность и была реализована. А вот для массивов с элементами значимых типов подобную же возможность не захотели или не смогли реализовать.
Сформулируем теперь точные правила, справедливые для присваивания и передачи аргументов в процедуру. Для того, чтобы было
Поделиться книгой в соц сетях:
Обратите внимание, что комментарий должен быть не короче 20 символов. Покажите уважение к себе и другим пользователям!