Материал: Объектно-ориентированное программирование на языке C#. Карпеев Д.О., Транин В.А

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

7. Структуры и перечисления

7.1. Тип enum

При построении той или иной системы зачастую удобно создавать набор символических имен, которые отображаются на известные числовые значения. Например, может понадобиться ссылаться на цвета определенного типа (Colors) с помощью таких констант, как Red (красный), Blue (синий), Yellow (жёлтый) и Green (зелёный). Для этой цели в С# поддерживается понятие специальных перечислений. Например, ниже показано специальное перечисление по имени Colors:

// Специальное перечисление.

enum Сolors

{

Red, // = О

Green, // = 1

Blue, // = 2

Yellow // = 3

}

В перечислении Colors определены четыре именованных константы, которые соответствуют дискретным числовым значениям. По умолчанию первому элементу присваивается значение 0, а всем остальным элементам значения присваиваются согласно схеме n+1. При желании исходное значение можно изменять как угодно. Нумерация в перечислениях вовсе не обязательно должна быть последовательной и содержать только уникальные значения.

По умолчанию для хранения значений перечисления используется тип System.Int32 (который в С# называется просто int); однако при желании его легко заменить. Перечисления в С# можно определять аналогичным образом для любых других ключевых системных типов (byte, short, int или long). Например, чтобы сделать для перечисления EmpType базовым тип byte, а не int, напишите следующий код:

//На этот раз Colors отображается на тип byte.

enum Colors : byte

{

Red = 10,

Green = 1,

Blue = 100,

Yellow = 9

}

Изменять базовый тип перечислений удобно в случае создания таких приложений .NET, которые будут развертываться на устройствах с небольшим объемом памяти (таких как поддерживающие .NET сотовые телефоны или устройства PDA), чтобы экономить память везде, где только возможно. Естественно, если для перечисления в качестве базового типа указан byte, каждое значение в этом перечислении ни в коем случае не должно выходить за рамки диапазона его допустимых значений.

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

При присваивании значения переменной перечисления перед значением должно обязательно указываться имя перечисления. Из-за того, что перечисления представляют собой фиксированные наборы пар "имя/значение", устанавливать для переменной перечисления значение, которое не определено напрямую в перечисляемом типе, не допускается.

Интересный аспект перечислений в .NET связан с тем, что функциональность они получают от типа класса System.Enum. В этом классе предлагается набор методов, которые позволяют опрашивать и преобразовывать заданное перечисление. Одним из наиболее полезных среди них является метод Enum.GetUnderlyingType(), который возвращает тип данных, используемый для хранения значений перечислимого типа.

Помимо метода Enum.GetUnderlyingType (), все перечисления С# также поддерживают метод по имени ToStringO, который возвращает имя текущего значения перечисления в виде строки. Ниже приведен соответствующий пример кода:

static void Main(string [ ] args)

{

Console.WriteLine ("**** Fun with Enums *****");

EmpType emp = EmpType.Contractor;

// Выводит строку "emp is a Contractor".

Console.WriteLine("emp is a {0}.", emp.ToString());

Console.ReadLine() ;

}

Чтобы выяснить не имя, а значение определенной переменной перечисления, можно просто привести ее к лежащему в основе типу хранения. Ниже показан пример, как это делается:

static void Main(string[] args)

{

Console.WriteLine("**** Fun with Enums *****");

EmpType emp = EmpType.Contractor;

// Выводит строку "Contractor = 100".

Console.WriteLine("{0} = {1}", emp.ToString() , (byte)emp);

Console.ReadLine();

}

7.2. Тип структур

Теперь рассмотрим использование типов структур (struct) в .NET. Типы структур прекрасно подходят для моделирования математических, геометрических и прочих "атомарных" сущностей в приложении. Они (как и перечисления) представляют собой определяемые пользователем типы, однако (в отличие от перечислений) просто коллекциями пар "имя/значение" - не являются. Вместо этого они скорее представляют собой типы, способные содержать любое количество полей данных и членов, выполняющих над ними какие-то операции.

В С# структуры создаются с помощью ключевого слова struct:

struct Point

{

// Поля структуры.

public int X;

public int Y;

// Добавление 1 к позиции (X, Y) .

public void Increment ()

{

X++; Y++;

}

// Вычитание 1 из позиции (X, Y) .

public void Decrement ()

{

X--; Y—;

}

// Отображение текущей позиции.

public void Display()

{

Console.WriteLine("X = {0}, Y= {1}", X, Y) ;

}

}

Здесь определены два целочисленных поля (X и Y) с использованием ключевого слова public, которое представляет собой один из модификаторов управления доступом. Объявление данных с использованием ключевого слова public гарантирует наличие у вызывающего кода возможности напрямую получать к ним доступ из данной переменной Point (через операцию точки).

Для создания переменной типа структуры на выбор доступно несколько вариантов.

Первый, когда просто создается переменная Point с присваиванием значений каждому из ее общедоступных элементов данных типа полей перед вызовом ее членов. Если значения общедоступным элементам структуры не присвоены перед ее использованием, компилятор сообщит об ошибке.

// Ошибка! Полю Y не было присвоено значение.

Point pi;

pl.X = 10;

pi.Display();

// Все в порядке1 Обоим полям были присвоены

// значения перед использованием.

Point p2;

р2.Х = 10;

p2.Y = 10;

р2.Display();

В качестве альтернативного варианта переменные типа структур можно создавать с применением ключевого слова new, поддерживаемого в С#, что предусматривает вызов для структуры конструктора по умолчанию. По определению используемый по умолчанию конструктор не принимает никаких аргументов. Преимущество подхода с вызовом для структуры конструктора по умолчанию состоит в том, что в таком случае каждому элементу данных полей автоматически присваивается соответствующее значение по умолчанию:

// Установка для всех полей значений по умолчанию

//за счет применения конструктора по умолчанию.

Point pi = new Point () ;

// Выводит Х=0, Y=0

pi.Display();

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

Основные различия классов и структур:

1) Класс и структура принадлежат к двум различным категориям типов – типов-ссылок и типов-значений. То есть присвоение одной структуры другой вызывает операцию копирования содержимого одной из них в другую, а после присвоения переменной типа класса другой приводит к тому, что обе они ссылаются на один объект;

2) Структуру нельзя наследовать от других структур или классов. Все структуры наследуются непосредственно от System.ValueType, который наследуется от System.Object.;

3) Структура не может служить основой для классов;

4) Структуры могут быть созданы без использования нового оператора.

8. Операторы и управляющие конструкции языка c#

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

8.1. Арифметические операторы

Арифметические операторы, представленные в языке С#, приведены ниже.

Оператор

Действие

+

Сложение

-

Вычитание, унарный минус

*

Умножение

/

Деление

%

Деление по модулю

++

Инкремент

--

Декремент

Операторы +, -, * и / действуют так, как предполагает их обозначение. Их можно применять к любому встроенному числовому типу данных.

Действие арифметических операторов не требует особых пояснений, за исключением следующих особых случаев. Прежде всего, не следует забывать, что когда оператор / применяется к целому числу, то любой остаток от деления отбрасывается; например, результат целочисленного деления 10/3 будет равен 3. Остаток от этого деления можно получить с помощью оператора деления по модулю (%), который иначе называется оператором вычисления остатка. Он дает остаток от целочисленного деления. Например, 10 % 3 равно 1. В С# оператор % можно применять как к целочисленным типам данных, так и к типам с плавающей точкой. Поэтому 10.0 % 3.0 также равно 1. В этом отношении С# отличается от языков С и C++, где операции деления по модулю разрешаются только для целочисленных типов данных.

Оператор инкремента увеличивает свой операнд на 1, а оператор декремента уменьшает операнд на 1. Оба оператора инкремента и декремента можно указывать до операнда (в префиксной форме) или же после операнда (в постфиксной форме). Когда оператор инкремента или декремента предшествует своему операнду, то результатом операции становится значение операнда после инкремента или декремента. А когда оператор инкремента или декремента следует после своего операнда, то результатом операции становится значение операнда до инкремента или декремента. Рассмотрим следующий фрагмент кода.

х = 10;

у = ++х;

В данном случае значение переменной у будет установлено равным И, поскольку значение переменной х сначала увеличивается на 1, а затем присваивается переменной у. Но во фрагменте кода

х = 10;

у = х++;

значение переменной у будет установлено равным 10, так как в этом случае значение переменной х сначала присваивается переменной у, а затем увеличивается на 1. В обоих случаях значение переменной х оказывается равным 11. Отличие состоит лишь том, когда именно это значение станет равным 11: до или после его присваивания переменной у.

8.2. Операторы отношения и логические операторы

Операторы отношения: == (равно), != (не равно), > (больше), < (меньше), >= (больше равно), <= (меньше равно).

Логические операторы: & (И), | (ИЛИ), ^ (исключающее ИЛИ), && (укороченное И), || (укороченное ИЛИ), ! (НЕ).

Результатом выполнения оператора отношения или логического оператора является логическое значение типа bool.

В целом, объекты можно сравнивать на равенство или неравенство, используя операторы отношения == и !=. А операторы сравнения <, >, <= или >= могут применяться только к тем типам данных, которые поддерживают отношение порядка. Следовательно, операторы отношения можно применять ко всем числовым типам данных. Но значения типа bool могут сравниваться только на равенство или неравенство, поскольку истинные (true) и ложные (false) значения не упорядочиваются. Например, сравнение true > false в С# не имеет смысла.

В С# предусмотрены также специальные, укороченные, варианты логических операторов И и ИЛИ, предназначенные для получения более эффективного кода. Поясним это на следующих примерах логических операций. Если первый операнд логической операции И имеет ложное значение (false), то ее результат будет иметь ложное значение независимо от значения второго операнда. Если же первый операнд логической операции ИЛИ имеет истинное значение (true), то ее результат будет иметь истинное значение независимо от значения второго операнда. Благодаря тому что значение второго операнда в этих операциях вычислять не нужно, экономится время и повышается эффективность кода.

Укороченная логическая операция И выполняется с помощью оператора &&, а укороченная логическая операция ИЛИ — с помощью оператора ||. Этим укороченным логическим операторам соответствуют обычные логические операторы & и |. Единственное отличие укороченного логического оператора от обычного заключается в том, что второй его операнд вычисляется только по мере необходимости. Укороченный оператор И называется также условным логическим оператором И, а укороченный оператор ИЛИ — условным логическим оператором ИЛИ.

Таблица истинности для логических И и ИЛИ:

A

B

A && B

A || B

0

0

0

0

0

1

0

1

1

0

0

1

1

1

1

1

8.3. Оператор присваивания

Оператор присваивания обозначается одиночным знаком равенства (=). В С# оператор присваивания действует таким же образом, как и в других языках программирования. Ниже приведена его общая форма.

имя_переменной = выражение

Здесь имя_переменной должно быть совместимо с типом выражения.

У оператора присваивания имеется одна интересная особенность, о которой вам будет полезно знать: он позволяет создавать цепочку операций присваивания. Рассмотрим, например, следующий фрагмент кода.

int х, у, z;

х = у = z = 100; // присвоить значение 100 переменным х, у и z

В приведенном выше фрагменте кода одно и то же значение 100 задается для переменных х, у и z с помощью единственного оператора присваивания. Это значение присваивается сначала переменной z, затем переменной у и, наконец, переменной х. Такой способ присваивания "по цепочке" удобен для задания общего значения целой группе переменных.

Для многих двоичных операций, т.е. операций, требующих наличия двух операндов, существуют отдельные составные операторы присваивания. Общая форма всех этих операторов имеет следующий вид:

имя_переменной ор = выражение

где ор — арифметический или логический оператор, применяемый вместе с оператором присваивания.

8.4. Оператор ?

Он представляет собой условный оператор и часто используется вместо определенных видов конструкций if-then-elsе. Оператор ? иногда еще называют тернарным, поскольку для него требуются три операнда. Ниже приведена общая форма этого оператора.

Выражение 1 ? Выражение2 : Выражение3;

Здесь Выражение1 должно относиться к типу bool, а Выражение2 и Выражение3 — к одному и тому же типу. Обратите внимание на применение двоеточия и его местоположение в операторе ?. Значение выражения ? определяется следующим образом. Сначала вычисляется Выражение1. Если оно истинно, то вычисляется Выражение2, а полученный результат определяет значение всего выражения ? в целом. Если же Выражение1 оказывается ложным, то вычисляется Выражение3, и его значение становится общим для всего выражения ?. Рассмотрим следующий пример, в котором переменной absval присваивается значение переменной val.

absval = val < 0 ? -val : val; // получить абсолютное значение переменной val

В данном примере переменной absval присваивается значение переменной val, если оно больше или равно нулю. Если же значение переменной val отрицательно, то переменной absval присваивается результат отрицания этого значения, что в итоге дает положительное значение.

8.5. Управляющие операторы

Управляющие операторы разделяются на три категории: операторы выбора, к числу которых относятся операторы if и switch, итерационные операторы, в том числе операторы цикла for, while, do-while и foreach, а также операторы перехода: break, continue, goto, return и throw.

а) Операторы выбора:

1. Оператор if

Полная форма этого оператора:

if(условие) оператор;

else оператор;

где условие — это некоторое условное выражение, а оператор — адресат операторов if и else. Оператор else не является обязательным. Адресатом обоих операторов, if и else, могут также служить блоки операторов. Ниже приведена общая форма оператора if, в котором используются блоки операторов.

if(условие)

{

последовательность операторов

}

else

{

последовательность операторов

}

Если условное выражение оказывается истинным, то выполняется адресат оператора if. В противном случае выполняется адресат оператора else, если таковой существует. Но одновременно не может выполняться и то, и другое. Условное выражение, управляющее оператором if, должно давать результат типа bool.

Вложенным называется такой оператор if, который является адресатом другого оператора if или же оператора else. Вложенные операторы if очень часто применяются в программировании. Что же касается их применения в С#, то не следует забывать, что любой оператор else всегда связан с ближайшим оператором if, т.е. с тем оператором if, который находится в том же самом блоке, где и оператор else, но не с другим оператором else.

Источник: https://studfile.net/preview/16562554/