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

}

}

Если рассмотреть альтернативный способ инициализации статической переменной, например, в теле конструктора(ов), то он окажется плохим, так как каждый раз при создании нового объекта, происходит переустановка общего для всех объектов режима сортировки. Теперь посмотрим, что у нас получилось, изменив содержимое функций CompareTo и Main.

int IComparable.CompareTo(object other)

{

switch (sortBy) // Выбираем ветвь в зависимости от режима сортировки

{

     // Вставьте ваш код . . .

}

}

static void Main()

{

//==== Создайте массив

Console.WriteLine("Men before Sort:\n");

  //==== Выведите массив

Array.Sort(men); // Для сортировки пользуемся статическим методом класса Array

Console.WriteLine("Men after Sort by name:\n");

  //==== Выведите массив

Man.sortBy = eSortBy.eAge; // Изменяем режим сортировки

Array.Sort(men);

Console.WriteLine("\n\nMen after Sort by age:\n");

  //==== Выведите массив

}

Механизм свойств в С#

Для того, чтобы лучше понять необходимость следующего шага в развитии класса Man, попробуйте ответить на вопрос. Что будет, если режим сортировки примет недопустимое значние? Это можно устроить, например таким образом:

public static eSortBy sortBy = (eSortBy)10;

Подобная выходка не приведет к отказу (исключению), благодаря надежной работе блока switch в CompareTo, но сортировка-то не будет выполнена. Проверьте сами.

Если мы сделаем переменную sortBy закрытой (private) и одновременно добавим открытое свойство, позволяющее (с предосторожностями) изменять ее значение, то поведение программы станет более логичным, а код более надежным. Итак свойство — это особый член класса. Для пользователя он выглядит как открытая для записи или для чтения (или для того и другого одновременно) переменная. Для разработчика — это пара открытых методов класса (или только один из методов — аксессоров). Вставьте в класс Man фрагмент, который объявляет и реализует свойство. Позже мы добавим и другие свойства.

public static eSortBy SortBy   // Свойство SortBy типа eSortBy

{

      // Вставьте ваш код . . .

}

Для управления статической переменной (sortBy) свойство тоже должно быть статическим. С точки зрения синтаксиса свойство выглядит как структура С++, в которую вложены два метода get и set, которые лучше называть аксессорами (они не тянут на методы по синтаксису). Аксессор get просто возвращает текущее значение переменной sortBy. Вы помните, что мы собирались сделать ее закрытой? Замените объявление, чтобы выполнить это.

Внутри аксессора set проверьте легальность того, что пытается выполнить пользователь, и выбросьте одно из стандартных исключений в случае, если он присваивает переменной (точнее, свойству) некорректное значение. При создании исключения используйте текст (например, "Wrong sorting mode: {0}", value). Для того, чтобы посмотреть как это работает, введите изменения в функцию Main.

static void Main()

{

Man

  ken = new Man("Ken Wood",40),

  len = new Man("Lennie Tristano",50),

  ben = new Man("Ben Webster",60);

Man[] men =  { ken, ben, ken, len  };

Array.Sort(men);

Console.WriteLine("Men after Sort by name:\n");

foreach (Man m in men)

  m.Out();

  //==== Работа со свойством

if (Man.SortBy == eSortBy.eName) // здесь работает get

  Man.SortBy = eSortBy.eAge;     // здесь работает set

Array.Sort(men);

Console.WriteLine("\n\nMen after Sort by age:\n");

foreach (Man m in men)

  m.Out();

Man.SortBy = (eSortBy)20;      // Здесь мы намеренно вызываем исключение

}

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

              Unhandled Exception: System.ArgumentOutOfRangeException:

              Specified argument was out of the range of valid values.

              Parameter name: Wrong sorting mode: 20

Текст последней строки мы создали сами. Он разъясняет реальную причину исключения. В качестве упражнения самостоятельно введите обработку исключения. Ранее было показано, как это делается.

Boxing и Unboxing

Про эти механизмы очень много и занудно говорят, но они должны быть тривиальны для Вас, так как вы знаете, как работает полиморфизм в С++. Покажем оба механизма на примере.

int i = -5;     // Простой (value) тип

object o = i; // Сложный (reference) тип    Эта процедура называется boxing.

Console.WriteLine("o = " + o);

Все типы происходят от Object. Вспомните, что папа (object) может показывать на ребенка (int). Здесь важно, что переменная i была в stack, а ее копия (в виде объекта o) перемещается в heap. Где-то (незаметно) сработала операция new object. Также важно, что переменная o — это ссылка (адрес) новой переменной типа reference, живущей в heap. Итак, копия переменной i перекочевала в heap, так как все reference types живут только там.

Почему boxing? Box — коробка, оболочка (wrapper). Reference-тип object является wrapper class (классом-оболочкой) или влиятельным благодетелем для простенькой переменной i. Итак более мощный объект скушал (поместил в коробку и переселил в heap) копию простой переменной.

Можно ли вновь сделать ее простой и возвратить в стек? Нет, ее уже никак не возвратить (она — пища для GC), но можно в стеке создать новую простую переменную и скопировать в нее значение живущего в heap влиятельного объекта. Эта процедура называется unboxing. Вот она.

int j = (int)o;     // int j = o;  не пройдет.  Дети не могут так поступать.

Console.WriteLine("j = " + j);

Процедура unboxing'а обязательно сопровождается приведением типов. Вспомните, вы это делали, когда присваивали указатель типа CShape (папа) указателю типа CRect (ребенок). Здесь — то же самое.