Содержание
Структура (struct) в C# — это пользовательский тип данных, который используется наряду с классами и может содержать какие-либо данные и методы. Структурами также являются такие типы данных как int, double и т.д. Одно из отличий структуры от класса заключается в том, что структура — это тип значений, а класс — это ссылочный тип.
Объявление структуры
Для того, чтобы объявить переменную типа структуры в C# используется ключевое слово struct:
struct имя_структуры
{
// элементы структуры
}
После ключевого слова struct следует имя структуры и далее, в фигурных скобках — элементы структуры (поля, методы и т.д.). Например, определим структуру, которая описывает точку в трехмерном пространстве:
public struct Point3D
{
public double X { get; set; }
public double Y { get; set; }
public double Z{ get; set; }
public override string ToString()
{
return $"({X},{Y},{Z})";
}
}
Наша структура содержит три свойства — координаты X,Y,Z и один переопределенный метод ToString, который возвращает строку с координатами точки. Обращение к полям, свойствам и методам структуры, как и в случае с классами, происходит с использованием имени переменной, например:
Point.X = 100; Point.Y = 100; Point.Z = 100; Console.WriteLine(Point.ToString());
здесь Point — это имя переменной типа Point3D.
Создание структуры
Как и в случае с классами, структуры в C# можно создавать с использованием ключевого слова new:
Point3D Point = new Point3D();
После того, как структура создана, её полям и свойствам можно присваивать значения (см. в предыдущем пункте).
Начиная с .NET 5 в C# 9 можно использовать краткую форму записи new:
Point3D Point = new();
Если структура содержит только публичные поля (не путать со свойствами) и методы, то можно не вызывать конструктор, а сразу назначить значение полей и после этого вызывать методы структуру. Например:
public struct Point3D
{
public double X;
public double Y;
public double Z;
public override string ToString()
{
return $"({X},{Y},{Z})";
}
};
//не вызываем конструктор, а сразу задаем значение полей
Point3D Point;
Point.X = 100;
Point.Y = 100;
Point.Z = 100;
Console.WriteLine(Point.ToString());
Начиная с версии C# 10 полям структуры можно присваивать значения по умолчанию, однако, в этом случае необходимо будет вызвать new(), чтобы создать экземпляр структуры. Например:
public struct Point3D
{
public double X = 10;
public double Y = 5;
public double Z = 8;
public override string ToString()
{
return $"({X},{Y},{Z})";
}
}
Если мы попытаемся вывести в консоль значения полей вот так:
Point3D point; Console.WriteLine(point.ToString());
то получим ошибку «Попытка доступа к неинициализированной переменной». Поэтому, необходимо получать доступ к переменной point с использованием оператора new:
Point3D point = new(); Console.WriteLine(point.ToString());
Конструкторы структур
Здесь, опять же, структуры ничем не отличаются от классов C#. У любой структуры есть как минимум один конструктор (конструктор по умолчанию) без параметров. При этом, мы можем создавать свои конструкторы и точно также, как и с классами вызывать их по цепочке, например:
public struct Point3D
{
public double X = 10;
public double Y = 5;
public double Z = 8;
public override string ToString()
{
return $"({X},{Y},{Z})";
}
public Point3D(double x) : this()
{
X = x;
Y = 0;
Z = 0;
}
public Point3D(double x, double y) : this(x)
{
Y = y;
Z = 0;
}
public Point3D(double x, double y, double z) : this(x, y)
{
Z = z;
}
}
Создаем структуры (struct)
Point3D point1 = new(10); Point3D point2 = new(10, 20); Point3D point3 = new(10, 30, 40); Console.WriteLine(point1); Console.WriteLine(point2); Console.WriteLine(point3);
Вывод консоли
Начиная с версии C# 10 мы можем также создать для структуры свой конструктор без параметров:
//конструктор без параметров
public Point3D()
{
X = 10;
Y = 20;
Z = 30;
}
Инициализатор структур struct
Значения полей и свойств структуры, как в случае и с классами, можно задавать непосредственно при создании, используя следующую языковую конструкцию:
Point3D Point4 = new() { X = 24, Y = 45, Z = 22 };
то есть вначале мы объявляем переменную, затем вызываем конструктор и затем в фигурных скобках указываем имена полей или свойств и их значения. Даже, если мы создадим и проинициализируем структуру вот так:
Point3D Point4 = new(10, 20, 30) { X = 24, Y = 45, Z = 22 };
Console.WriteLine(Point4);
то значения полей будут теми, которые мы указываем в инициализаторе, т.е. 24, 45 и 22.
Копирование структур с изменением значений (оператор with)
Начиная с версии C# 10 мы можем копировать значения структур с изменениями, например:
Point3D Point4 = new() { X = 24, Y = 45, Z = 22 };
Point3D Point5 = Point4 with { X = 100 };//меняем значение X
Console.WriteLine(Point4);
Console.WriteLine(Point5);
Вывод в консоли:
(100,45,22)
Отличие структуры (struct) от класса в C#
Думаю, что после прочтения всего, что было выше, у любого начинающего программировать в C# человека возникнет резонный вопрос: если у структур в C# всё тоже самое, что и у классов, то зачем нам эти структуры нужны и, если всё-таки они нужны, то когда их использовать? Попробуем в кратце разобраться с этим вопросом вместе.
Структура — тип значений, класс — ссылочный тип
Если не вдаваться далеко в подробности работы программ, то основное отличие struct от class заключается в том, что структура храниться целиком в стеке, а объект класса храниться в куче, а ссылка на него — в стеке. В результате этого, доступ к данным структуре будет путь не намного, но быстрее, чем к классу. О том, что такое стек и куча мы ещё поговорим позднее.
Структуры не поддерживают наследование
В отличие от классов C#, наследование структур не поддерживается, то есть вот такой код приведет к ошибке:
struct Point3DType2 : Point3D
{ }
Когда использовать структуры (struct), а когда классы (class) в C#
Конечно, вопрос о том, что лучше использовать зависит, в первую очередь, от того в контексте чего задается такой вопрос, но основная рекомендация от Microsoft может быть сформулирована следующим образом: структуры (struct) стоит использовать в том случае, если ваш объект содержит минимальное количество каких-либо логически связанных операций или не содержит их вообще.
Например, использование структур вполне оправдано в примерах выше — описание точки в трехмерном пространстве. Максимум логики, которую мы можем добавить в структуру — это переопределить операторы сложения, вычитания и равенства.
Если же мы пробуем описать с помощью своего типа данных, например, автомобиль, то тут уже логика может быть самая разветвленная: проверка наличия топлива в баке, технические характеристики, оценка состояния в зависимости от каких-либо внешних или внутренних факторов и т.д. Соответственно, в этом случае, более предпочтительным будет использование не структуры, а класса.
Итого
Как и классы в C#, структуры позволяют определить пользовательский тип данных в вашем проекте. При этом, на первый взгляд, структуры (struct) практически ни чем не отличаются от классов, однако различия есть: во-первых, структуры относятся к типам значений, во-вторых, структуры не поддерживают механизмов наследования. Использовать или не использовать структуры — решение самого разработчика, однако, рекомендуется использовать тип struct только в том случае, если ваш пользовательский тип данных содержит минимум бизнес-логики или не содержит её вовсе.
