Опубликован: 02.12.2009 | Уровень: специалист | Доступ: платный | ВУЗ: Тверской государственный университет
Лекция 4:

Перечисления

< Лекция 3 || Лекция 4: 12345 || Лекция 5 >
Аннотация: Еще один важный частный случай класса – перечисление позволяет задать разработчику конечное множество значений, которые могут получать объекты классы. Показано, как перечисление, заданное шкалой, позволяет строить эффективные по памяти и времени алгоритмы для широкого класса задач. Лекция сопровождается задачами.

Проект к данной лекции Вы можете скачать здесь.

Классы перечисления

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

[атрибуты][модификаторы]enum имя_перечисления[:базовый класс]
{список_возможных_значений}

Описание атрибутов отложим на последующие лекции. Модификаторами могут быть четыре известных модификатора доступа и модификатор new. Ключевое слов enum (сокращение слова enumeration ) говорит, что определяется частный случай класса - перечисление. Список возможных значений задает те значения, которые могут получать объекты этого класса. Возможные значения должны быть идентификаторами. Как и всюду в C#, при построении идентификаторов допускаются не только символы латиницы, но и символы кириллицы. Для имен переменных и других программных сущностей принято использовать латиницу, для элементов перечисления использование слов русского языка является нормой. Идентификаторы являются константами, называемыми перечислителями ( enumerator ), а само перечисление можно рассматривать как список перечислителей.

Идентификаторы, заданные в перечислении, как правило, имеют содержательный смысл и являются именами сущностей предметной области. Они используются для организации интерфейса пользователя, при вводе и выводе, при описании действий над объектами перечисления. В памяти компьютера элементы перечисления представлены числами, так что перечисление является числовым типом данных, а не символьным, как могло бы казаться. Множество элементов перечисления проецируется на целочисленный тип данных. По умолчанию элементы перечисления проецируются на начальный отрезок типа int. В этом случае при описании перечисления из n идентификаторов первый из идентификаторов отображается в число 0, последний - в число n-1. Значения, заданные списком, проецируются на плотное подмножество базового класса.

Процессом отображения перечисления на числовой тип данных можно управлять. Базовый класс, который задан при описании перечисления, указывает, в какой тип отображается перечисление. Понятно, что в качестве базового класса можно указывать только классы, задающие целочисленный тип. Кроме типа char, любой целочисленный тип может выступать в роли базового класса. При желании можно задать не только базовый класс, но и интервал представления внутри базового класса. Если для первого идентификатора в списке указать целочисленное значение, то оно будет использоваться как указание задания левой границы интервала представления. Все остальные значения будут проецироваться на интервал с заданной левой границей.

Более того, множество целых, на которое проецируется список элементов перечисления, вовсе не обязано быть плотным. Числовое значение, на которое будет проецироваться данный идентификатор, можно задать для каждого идентификатора. Эта крайне важная и полезная особенность перечислений, активно используемая программистами при работе со шкалами. О шкалах мы говорили при рассмотрении логических операций над целыми числами. Обсудим их подробно и в данной лекции.

Приведу примеры объявлений классов-перечислений:

public enum Profession{teacher, engineer, businessman};
public enum MyColors {red, blue, yellow, black, white};
public enum TwoColors {black, white};
public enum Rainbow {красный, оранжевый, желтый, зеленый, голубой, синий, фиолетовый};
public enum Sex: byte {man=1, woman};
public enum Days:long {Sun,Mon,Tue,Wed,Thu, Fri, Sat};

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

  • Как и другие классы, перечисления могут быть объявлены непосредственно в пространстве имен проекта или могут быть вложены в описание класса. Последний вариант часто применяется, когда перечисление используется в интересах одного класса. В этом случае оно может иметь модификатор доступа private.
  • Константы разных перечислений могут совпадать, как в перечислениях MyColors и TwoColors. Имя константы всегда уточняется именем перечисления.
  • Константы могут задаваться словами русского языка, как в перечислении Rainbow.
  • Разрешается задавать базовый класс перечисления. Для перечисления Sex базовым классом является byte, а для перечисления Days - класс long.
  • Разрешается не только задавать базовый класс, но и указывать начальный элемент подмножества, на которое проецируется множество значений перечисления. Для перечисления Sex в качестве базового класса выбран класс byte, а подмножество значений начинается с 1, так что хранимым значением константы man является 1, а woman - 2.

Рассмотрим теперь пример работы с объектами - экземплярами различных перечислений. С этой целью введем в программный проект класс Testing - он является клиентом выше определенных перечислений, методы которого реализуют тесты, позволяющие анализировать работу с перечислениями.

/// <summary>
/// Методы класса позволяют тестировать
/// перечисления - создавать и обрабатывать
/// объекты перечислений, определенных в проекте
/// </summary>
class Testing
{
    /// <summary>
    /// Создание объектов перечислений,
    /// присваивание значений и вывод на печать
    /// </summary>
    public void TestEnum()
    {
        const string COLOR_EQUAL =
            "Цвета совпадают!";
        const string COLOR_DIFFERENT =
            "Цвета не совпадают!";
        const string ENUM_RAINBOW =
            "Цвета перечисления Rainbow:";

        Rainbow color = new Rainbow();
        //MyColors color1 = MyColors(MyColors.blue);
        MyColors color1 = MyColors.white;
        TwoColors color2;
        color2 = TwoColors.white;
        Console.WriteLine("цвет1 = {0}, цвет2 = {1}",
            color1, color2);

        //if(color1 != color2) color2 = color1;
        if (color1.ToString() == color2.ToString())
            Console.WriteLine(COLOR_EQUAL);
        else Console.WriteLine(COLOR_DIFFERENT);
        
        Rainbow color3;
        color3 = (Rainbow)4;
        color1 = MyColors.blue;
        Console.WriteLine("цвет1 = {0}, цвет2 = {1}",
            color1, color3);
        if (color3 == Rainbow.голубой) 
            Console.WriteLine(COLOR_EQUAL);
        else Console.WriteLine(COLOR_DIFFERENT);

        Console.WriteLine(ENUM_RAINBOW);
        for (int num = 0; num < 10; num++)
        {
            color = (Rainbow)num;
            Console.WriteLine(color.ToString());
        }

        Sex who = Sex.man;
        Days first_work_day = (Days)(long)1;
       
        Console.WriteLine("who={0}, first_work_day={1}",
            who, first_work_day);
    }
}

Данный пример иллюстрирует следующие особенности работы с объектами перечислений.

  • Объект перечисления можно создать в объектном стиле с использованием операции new. Но у перечислений есть только единственный конструктор без параметров, инициализирующий объект первым по порядку следования значением перечисления. В примере так создается объект color класса Rainbow, получающий значение "красный".
  • Попытка создать объект color1 класса MyColors закомментирована, поскольку у перечислений нет конструкторов с параметрами.
  • Объекты можно объявлять с явной инициализацией, как color1, или с отложенной инициализацией, как color2. При объявлении без явной инициализации объект получает значение первой константы перечисления, так что color2 в момент объявления получает значение black.
  • Объекту можно присвоить значение, которое задается константой перечисления, уточненной именем перечисления, как для color1 и color2.
  • Нельзя сравнивать объекты разных перечислений, например, color1 и color2. Это разные классы, для которых не определены операции преобразования типа. Но, заметьте, можно сравнивать строки, возвращаемые методом ToString, например, color1.ToSting() и color2.ToString().
  • Метод ToString, наследованный от класса object, для перечислений переопределен. Если для числового значения объекта существует константа перечисления, отображаемая на числовое значение, то в качестве результата возвращается соответствующий идентификатор как строка. В противном случае, когда такого отображения нет, возвращается как строка само числовое значение.
  • Существуют явные взаимно обратные преобразования констант базового типа и констант перечисления. Цикл, печатающий все значения перечисления Rainbow, демонстрирует преобразование объектов типа int в объекты Rainbow. Заметьте, в объекты Rainbow преобразуются числа, выходящие за интервал, на который проецируются константы перечисления.

Результаты работы метода TestEnum показаны на рис. 3.1.

Результаты работа теста, демонстрирующего работу с объектами перечислений

Рис. 3.1. Результаты работа теста, демонстрирующего работу с объектами перечислений

Персоны и профессии

Рассмотрим еще один пример работы с перечислениями, приближенный к реальности. Рассмотрим класс Person с полями, типичными для классов, которые описывают личность, - имя, фамилия, возраст и так далее. Добавим в этот класс поле, определяющее профессию персоны. Вполне разумно иметь перечисление, например, Profession, задающее список возможных профессий. Сделаем это поле, как обычно, закрытым, а доступ к нему обеспечим соответствующим свойством:

Profession prof;
public Profession Prof
 {
   get {return (prof);}
   set {prof = value;}
 }

Добавим еще в класс Person метод Analysis, который анализирует профессию, организуя традиционный разбор случаев и принимая решение на каждой ветви, в данном примере - выводя соответствующий текст:

public void Analysis()
 {
    switch (prof)
     {
      case Profession.businessman:
        Console.WriteLine ("профессия: бизнесмен");
        break;
      case Profession.teacher:
        Console.WriteLine ("профессия: учитель");
        break;
      case Profession.engineer:
        Console.WriteLine ("профессия: инженер");
        break;
      default:
        Console.WriteLine ("профессия: неизвестна");
        break;
     }
 }

Приведу простой тестирующий пример работы с объектом Person и его профессией:

public void TestProfession()
 {
   Person pers1 = new Person ("Петров");
   pers1.Prof = Profession.teacher;
   pers1.Analysis();
 }

Рассмотрим перечисление Status, элементы которого задают возможный статус персоны:

public enum Status
  {
    ребенок, школьник,
    студент, работник, пенсионер
  }

Зададим в классе Person поле status, принадлежащее перечислению Status:

Status status = Status.студент;

Предположим, что статус персоны изменяется с возрастом, определив метод-свойство для поля age следующим образом:

/// <summary>
///стратегия: Read,Write (Чтение, запись)
/// </summary>
public int Age
{
  set
  {
     age = value;
     //Изменение статуса
     if (age < 7) status = Status.ребенок;
     else if (age < 17) status = Status.школьник;
     else if (age < 22) status = Status.студент;
     else if (age < 65) status = Status.работник;
     else status = Status.пенсионер;
  }
  get { return (age); }
}

Соответствующий тест, демонстрирующий работу с полем status, имеет вид:

public void TestStatus()
  {
     Person pers = new Person("Кузнецов");
     pers.Age = 5;
     Console.WriteLine("возраст = {0}, статус = {1}",
         pers.Age, pers.GetStatus);
     pers.Age = 20;
     Console.WriteLine("возраст = {0}, статус = {1}",
         pers.Age, pers.GetStatus);
     pers.Age = 35;
     Console.WriteLine("возраст = {0}, статус = {1}",
         pers.Age, pers.GetStatus);
  }

В этом примере GetStatus - это метод-свойство, обеспечивающий доступ к закрытому полю status. Результаты работы с объектами перечислений, полученные при вызове тестов TestProfession и TestStatus, показаны на рис. 3.2.

 Результаты работы с перечислениями Profession и Status

Рис. 3.2. Результаты работы с перечислениями Profession и Status
< Лекция 3 || Лекция 4: 12345 || Лекция 5 >
Федор Антонов
Федор Антонов

Здравствуйте!

Записался на ваш курс, но не понимаю как произвести оплату.

Надо ли писать заявление и, если да, то куда отправлять?

как я получу диплом о профессиональной переподготовке?

Илья Ардов
Илья Ардов

Добрый день!

Я записан на программу. Куда высылать договор и диплом?

Дмитрий Штаф
Дмитрий Штаф
Россия
Дмитрий Слапогузов
Дмитрий Слапогузов
Россия, Бийск