Исследование MS Visual С#, страница 5

Обработка исключения

Зададимся целью самостоятельно обработать исключение, вызываемое погрешностями ввода числовой информации. Для этого создадим две вспомогательные функции, одна из которых (MakeInt) будет следить за вводом, а другая (AskInt) позволит зациклить запрос с целью добиться корректного ввода. Эти функции носят общий характер, поэтому хочется сделать их доступными для всех классов, которые вскоре появятся в нашем пространстве имен ManSpace.

В C# отсутствует понятие глобальных сущностей. Здесь все функции и данные должны быть инкапсулированы в классы. Если функция или элемент данных имеют общий характер, то есть для их использования не требуется предварительно создавать и инициализировать объекты каких-то известных классов, то ваш выбор должен пасть на члены класса типа static. Из курса ООП вы, вероятно, знаете, что static–данные являются общими для всех объектов класса, они хранятся в единственном экземпляре. Вы также знаете, что static–функции могут быть вызваны по имени класса, то есть для их корректного вызова не требуются объекты класса.

Такое поведение вполне отвечает нашим интересам. Теперь осталось решить, надо ли помещать наши функции в существующий класс Man или их следует поместить в какой-то новый класс. Их, вероятно, можно поместить и в класс ManTest. В процессе выработки решения следует задать вопрос, является ли функция надежного ввода неотъемлемой частью класса Man? Очевидным ответом будет — нет. Такой ввод может быть использован и другими компонентами программы. Поэтому целесообразно создать новый вспомогательный класс (назовем его Helper), который будет содержать набор неких служебных public static-функций. Вставьте его внутрь (и в начало) блока, образующего пространство имен ManSpace.

class Helper  // Вспомогательный класс

{

public static int AskInt(      // Запрос на ввод целого числа

     string prompt,   // Текст подсказки

     int min,         // Ограничение снизу

     int max)         // Ограничение сверху

{

  bool ok = false;   // Пессимистический прогноз

  int res = 0;       // Возвращаемый результат

  while (!ok)        // Приставучий цикл

  {

     Console.Write (prompt);             // Подсказка

     string s = Console.ReadLine();// Помещаем ввод в буфер

     res = MakeInt (s, min, max); // Пытаемся преобразовать в целое число

     ok = res >= min; // При неудаче MakeInt возвращает число < min

  }

  return res;

}

//===== Преобразование в целое и обработка исключения

public static int MakeInt (string s, int min, int max)

{

  int res;

  try             // Попытка выполнить ненадежный код

  {

     res = int.Parse(s);     // Эта функция может выдать исключение

     if (res < min || max < res) // Попало ли число в допустимый диапазон?

     {

       Console.WriteLine("Value must be in ({0}, {1})",min,max);

       res = min - 1;

     }

  }

  catch  // Ловим все типы исключений

  {

     Console.WriteLine("It is not valid integer");

     res = min - 1;      // Возвращаем признак ошибки

  }

  return res;

}

}

Проверьте работу новых функций, изменив тело метода In в классе Man.

public void In()

{

Console.Write("Name: ");

name = Console.ReadLine();

age = // Вызовите функцию надежного ввода

}

Операция приведения к типу uint должна быть явно указана, так как неявное преобразование компилятор сочтет недопустимым (и правильно сделает, так как оно может быть ошибочным).

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

Ранг одномерного массива имеет значение 1, двухмерного — 2 и т. д. Каждое измерение массива имеет свой размер (Length). Оба эти параметра могут быть получены с помощью механизма свойств, который мы вскоре рассмотрим более подробно. Создайте новый файл, в котором вы исследуете работу с массивом объектов. Здесь мы объявим и инициализируем массив ссылок на объекты класса Man и продемонстрируем использование нового цикла foreach.

static void Main()

{

Man[] men =     // Объявляем массив ссылок на объекты класса Man

{

  new Man("Sam Lamb",32),

  new Man("Graham Dumb",12),

  new Man("Alan Calm",52)

};

foreach (Man m in men) // Один из 4-х типов циклов в С#

  m.Out();

  Console.WriteLine("\nArray Rank = {0} ",men.Rank); // Хотим увидеть фундаментальные свойства

Console.WriteLine("Array Length = {0} ",men.Length);

}

Если бы не местоположение квадратных скобок, то объявление массива могло бы сойти за объявление на С++. Но главным отличием от С++ здесь все же является не местоположение скобок, а тот факт, что с их помощью мы декларируем динамический массив адресов (читай, ссылок) на объекты класса Man, а не фиксированный массив самих объектов, как это было бы в С++. Чтобы убедиться в этом вы можете добавить в конец функции Main такой код:

men = new Man[2];

Console.WriteLine("Array Length = {0} ",men.Length);

Итак, men показывает на других людей. Но, куда делись предыдущие люди (элементы массива)? Здесь полезно нарисовать на бумаге структуру данных и связи (указатели, точнее ссылки). Первый массив из трех ссылок, а также три объекта класса Man, на которые он ссылался, стали мусором, который уберет garbage collector. Когда он это сделает?

Для ответа на этот вопрос достаточно поставить точку останова (F9) на единственной строке кода в теле деструктора класса Man и запустить приложение в режиме отладки (F5). Когда выполнение дойдет до точки останова посмотрите в окно Autos в блоке окон внизу экрана. Если вам не удалось его найти, то дайте команду Debug4Windows4Autos. В этом окне вы должны видеть имена погибающих объектов. Каждое последующее нажатие F5 демонстрирует следующий объект, для которого был вызван деструктор. Но кто вызывает деструкторы?

На этот вопрос даст ответ окно отладчика Call Stack, в котором вы наблюдаете цепочку вызовов функций, хранящуюся в стеке. Активизируйте его способом, аналогичным тому, что вы применняли для окна Autos, и убедитесь, что в нем присутствует строка: