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

Симметричную функцию записи данных в файл — Write — не обязательно делать виртуальной, нас опять выручит уже существующая виртуальная функция ToString. Достаточно объявить в классе Man метод:

public void Write (StreamWriter sw) // Вывод данных в выходной поток (файл)

{

  sw.WriteLine (this);

}

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

¨  private static int maxName — максимальная длина текста для поля name,

¨  private static int maxAge — максимальное значение для поля age,

¨  private static int manCounter — счетчик всех объектов, произведенных от класса Man, и существующих в данный момент.

Для того, чтобы пользователь мог управлять закрытыми данными, целесообразно ввести в класс статические и обычные свойства (properties). Статические — для управления статическими данными, а обычные — обычными. Для приобретения навыка в использовании свойств попробуйте добавить в класс Man пять новых свойств. Мы уже показывали как это делается, когда вводили свойство для управления режимом сортировки коллекции. Найдите его в описании класса. Оно определено как: public static int SortBy. Используйте аналогичную технику для создания следующих свойств:

¨  public string Name — для чтения (get) и записи (set) поля name,

¨  public uint Age — для чтения (get) и записи (set) поля age,

¨  public static int ManCounter — только для чтения (get) нового поля manCounter,

¨  public static int MaxName — для чтения (get) и записи (set) нового поля maxName,

¨  public static int MaxAge — для чтения (get) и записи (set) нового поля maxAge,

Каждый уважающий себя класс должен иметь в своем составе конструктор копирования. Поэтому, в дополнение к двум имеющимся конструкторам класса Man добавьте еще один, который принимает в качестве параметра объект собственного класса и копирует все его данные в свои поля. Например,

public Man (Man m)

{

  name = m.name;   age  = m.age;

  manCounter++;

}

Для иллюстрации нового для нас понятия delegate (со старым смыслом указателя на функцию) нам понадобятся еще два статических метода класса Man:

¨  public static void Surname(Man m) — вывод в консольное окно последнего слова из, возможно, длинной текстовой строки поля name,

¨  public static void Firstname(Man m) — вывод в консольное окно первого слова из, возможно, длинной текстовой строки поля name,

Реализация этих функций потребует знаний класса String из пространства имен System. Он достаточно удобен и неплохо описан в документации студии. Метод Surname, например, может быть реализован так:

public static void Surname(Man m)

{

      //===== Ищем последний пробел, считая, что фамилия расположена после него

  Console.WriteLine("  {0}", m.name.Substring(m.name.LastIndexOf(' ')+1));

}

Метод Firstrname за вами.

public static void Firstname(Man m)

{

//===== Найдите первый пробел, считая, что имя расположено до него

}

В области файловых операций библиотека .Net Framework Classes имеет много новых, приятных деталей, но для того, чтобы получить единый формат файлов с данными о людях, надо иметь более или менее единый подход к их реализации. Рекомендую подстроиться под такой формат файла данных.

MySoft, Man List v. 1.0

Stud: Alex Black     ; Age: 20;  Course: 3

Prof. Peter Pen      ; Age: 40;  Publications: 100

Первая строка позволяет отличить наш файл от всех других txt-файлов. Вы можете использовать любой другой формат, даже XML, но тогда самостоятельно доведите дело до конца. Практика показывает, что далеко не все успешно справляются с файловыми операциями, так как они требуют особой аккуратности, поэтому приведу еще один метод класса Man.

public virtual void Read (string line) // Чтение данных из входной строки

{

  string[] tokens = line.Split ( new char[] {':',';'});

  Name = tokens[1].Trim();

  age  = (uint)Helper.MakeInt (tokens[2], 1, maxAge);

}

Здесь использован неподражаемый метод Split класса string, который позволяет разбить текстовую строку на отдельные лексемы (tokens), используя массив разделителей. Сам массив создается по ходу дела. Этот прием является характерным для .NET и отличает современный подход от того, что мы видели в С++. В классе Stud переопределенная версия метода Read может иметь вид.

public override void Read (string line)

{

  base.Read (line);

  string[] tokens = line.Split ( new char[] {':',';'});

  course = (uint)Helper.MakeInt (tokens[4], 1, 6);

}

Класс для управления списком

Для реализации динамического списка следует создать класс List. Среди его методов, очевидно, должны быть: вставка в список, удаление из него, вывод всего списка, файловые операции ввода-вывода, сортировка по одному из общих полей, уничтожение списка. В число его данных целесообразно ввести какую-нибудь динамическую структуру данных, которая, собственно, и будет хранить ссылки на объекты классов, производных от класса Man. Кроме того, мы добавим несколько вспомогательных переменных, которые будут характеризовать текущее состояние списка.

В этот момент надо засучить рукава, дополнить класс Man указанными выше компонентами и приступить к созданнию динамического списка полиморфных объектов (класса List). Так как при реализации методов класса List наибольшие затруднения возникают с файловыми операциями, то дадим несколько подсказок, которые вы можете игнорировать и реализовать свой собственный подход. Для тех, кто будет пользоваться подсказками заметим, что в методах есть пропуски, которые вы должны заполнить.

class List

{

private ArrayList men;   // Список ссылок на объекты

private bool bModified;  // Флаг состояния: список изменен

private bool bNew;       // Флаг состояния: новый список