Урок 22. Структуры C#


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

Что такое структура?

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

Члены структуры

Помимо свойств и методов, структуры могут содержать множество других открытых и закрытых элементов. К ним относятся:
  • Конструкторы. В структуре можно создать несколько конструкторов. Однако конструктор по умолчанию, не содержащий параметров, не может быть определен. Конструктор по умолчанию является неявным для всех структур и не может быть переопределен. Он просто устанавливает все поля в структуре к их значениям по умолчанию.
  • Константы. В структуре могут быть определены как константы, так и перечисления.
  • Поля. Поля - это переменные, определенные в блоке кода структуры, которые доступны всем ее членам. Поля могут быть объявлены как публичные или частные, хотя публичные поля не поощряются, поскольку они ограничивают инкапсуляцию. В отличие от полей внутри классов, структурные поля не могут быть инициализированы в их коде объявления. Если требуется инициализация, это должно быть выполнено в конструкторе.
  • Индексаторы. Структура может включать индексатор для обеспечения функциональности, подобной массиву.
  • Операторы. Чтобы разрешить структурам вести себя как собственные типы данных, можно перегружать их операторы. Это позволяет операторам, таким как + и -, использоваться с экземплярами структуры.
  • События. Структуры могут включать объявления о событиях, позволяющие структуре направлять уведомления о принятых мерах или достигнутых состояниях. При использовании событий внутри структур необходимо соблюдать осторожность. Поскольку структуры являются типами значений, можно подписаться на событие в пределах копии экземпляра структуры, которая позже выходит за пределы области действия. Это может привести к тому, что вызванное событие неожиданно не будет захвачено.
Дополнительные ограничения

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

Отсутствие наследования несколько компенсируется тем, что структуры могут реализовать один или несколько интерфейсов. Это допускает определенную степень полиморфизма.

Еще одним ограничением структур является то, что они не могут включать деструктор. Деструктор не нужен, так как компилятор автоматически уничтожает экземпляры структур.

Когда использовать структуры

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

Облегченный класс находится в неблагоприятном положении по сравнению с аналогичной структурой в двух отношениях. Во-первых, больше памяти используется одним экземпляром класса, потому что и данные, и ссылка на эти данные должны быть сохранены. Структурная переменная не нуждается в хранении дополнительной справочной информации, поэтому имеет меньший объем памяти "следа". Во-вторых, весь доступ к свойствам структуры может быть сделан непосредственно. С объектом, основанным на классе, доступ является косвенным, используя сначала ссылку. Каждый из этих недостатков является незначительным для использования отдельных переменных. Однако при выполнении многих тысяч или миллионов операций совокупный эффект может быть значительным.

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

Создание структуры

Чтобы продемонстрировать использование структур, мы создадим тот, который представляет собой вектор. Эта простая структура будет включать свойства X и Y и метод, который вычисляет длину вектора. Для начала создайте новое консольное приложение с именем "CSharpStructures "и добавьте в проект новый файл класса с именем" Vector".

Синтаксис

Синтаксис для объявления структуры аналогичен синтаксису класса. Однако вместо использования ключевого слова class имя структуры имеет префикс "struct". Таким образом, общий синтаксис для простой структуры является:

struct структура-имя {}
Как описано выше, структуры могут реализовывать один или несколько интерфейсов. При необходимости имена интерфейсов добавляются к объявлению в виде списка, разделенного запятыми. Имя первого интерфейса имеет префикс в виде двоеточия (:).

структура структуры-имя: interface-1, interface-2,..., интерфейс-x {}
В зависимости от предпочтительной среды разработки, созданный векторный кодовый файл может содержать определение класса. Если это так, удалите его. Добавьте новое определение структуры в файл кода следующим образом:

struct Vector
{
}
Добавление свойств

Новая структура требует свойств для хранения координат X и Y. Они будут содержать целочисленные значения. Синтаксис для создания свойства идентичен синтаксису для создания свойства класса. Однако, если связать свойство с частным полем, как показано ниже, поле не может быть инициализировано в его объявлении.

Чтобы создать эти два свойства, добавьте следующий код в блок кода структуры:

private int _x, _y;
 
public int X
{
    get { return _x; }
    set { _x = value; }
}
 
public int Y
{
    get { return _y; }
    set { _y = value; }
}
Добавление метода

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

public double CalculateLength()
{
    return Math.Sqrt(_x * _x + _y * _y);
}
Создание экземпляра структуры

Существует два способа, с помощью которых можно создать экземпляр структуры и создать переменную. Первый способ - использовать оператор "new" и создать переменную, используя тот же синтаксис, что и при создании объекта класса. Новое ключевое слово вызывает выполнение конструктора в соответствии с используемыми параметрами. В следующем примере новый вектор создается с помощью конструктора по умолчанию. Это означает, что при первом создании поля _x и _y присваиваются их значения по умолчанию равны нулю, пока не будут изменены.

Vector v = new Vector();
Console.WriteLine("X: {0}\tY: {1}", v.X, v.Y);
Console.WriteLine("Length: {0}", v.CalculateLength());
 
v.X = 30;
v.Y = 40;
Console.WriteLine("X: {0}\tY: {1}", v.X, v.Y);
Console.WriteLine("Length: {0}", v.CalculateLength());
 
/* 
 
X: 0    Y: 0
Length: 0
X: 30   Y: 40
Length: 50
 
*/
Создание экземпляра структуры без оператора new

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

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

struct Vector
{
    public int X, Y;
 
    public double CalculateLength()
    {
        return Math.Sqrt(X * X + Y * Y);
    }
}
Теперь мы можем создать экземпляр векторной структуры без нового ключевого слова следующим образом:

Vector v;
v.X = 30;
v.Y = 40;
Console.WriteLine("X: {0}\tY: {1}", v.X, v.Y);
Console.WriteLine("Length: {0}", v.CalculateLength());
 
/* 
 
X: 30   Y: 40
Length: 50
 
*/
Добавление конструктора

Последний тип элемента, который мы продемонстрируем на этом уроке, является конструктором. Как и в случае с классами, в структуру можно добавить несколько конструкторов, чтобы инициализировать ее различными способами. Выбор конструктора для выполнения при создании экземпляра класса зависит от сигнатуры, используемой с ключевым словом "new".

Чтобы показать использование нового конструктора, добавьте следующее объявление в блок кода структуры. Этот конструктор позволит установить две координаты при создании экземпляра:

public Vector(int x, int y)
{
    X = x;
    Y = y;
}
Мы можем протестировать новый конструктор, используя следующий код:

Vector v = new Vector(30, 40);
Console.WriteLine("X: {0}\tY: {1}", v.X, v.Y);
Console.WriteLine("Length: {0}", v.CalculateLength());
 
/* 
 
X: 30   Y: 40
Length: 50
 
*/
Примечание: в отличие от определений классов, добавление нового конструктора не удаляет доступность конструктора по умолчанию.

Структуры - это типы значений

Как было описано ранее, основное различие между классами и структурами заключается в том, что структуры являются типами значений. Теперь, когда у нас есть структура, это можно продемонстрировать. В следующем коде создается экземпляр вектора и создается его копия. Когда поля копии изменяются, это никак не влияет на исходную структуру. Это отличается от использования классов и их объектов, так как каждая переменная будет ссылкой на одни и те же данные. Если бы данные в одном объекте должны были измениться, данные в другом объекте также были бы изменены.

Vector v1 = new Vector(30, 40);
Vector v2 = v1;
 
v2.X = 10;
v2.Y = 15;
 
Console.WriteLine("X1: {0}\tY1: {1}", v1.X, v1.Y);
Console.WriteLine("X2: {0}\tY2: {1}", v2.X, v2.Y);
 
/* Вывод
 
X1: 30  Y1: 40
X2: 10  Y2: 15
 
*/
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегистатьи IT, си шарп, ООП




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




Ошибки 500 (internal server error) и 550 (permission denied)
Как понять, почему не работает форма обратной связи на MODx
Совпадение стилей, или решение проблемы с OpenCart