Структуры (struct) в C#

Структура (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);

Вывод консоли

Структуры (struct) в C# и .NET

Начиная с версии 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);

Вывод в консоли:

(24,45,22)
(100,45,22)

Отличие структуры (struct) от класса в C#

Думаю, что после прочтения всего, что было выше, у любого начинающего программировать в C# человека возникнет резонный вопрос: если у структур в C# всё тоже самое, что и у классов, то зачем нам эти структуры нужны и, если всё-таки они нужны, то когда их использовать? Попробуем в кратце разобраться с этим вопросом вместе.

Структура — тип значений, класс — ссылочный тип

Если не вдаваться далеко в подробности работы программ, то основное отличие struct от class заключается в том, что структура храниться целиком в стеке, а объект класса храниться в куче, а ссылка на него — в стеке. В результате этого, доступ к данным структуре будет путь не намного, но быстрее, чем к классу. О том, что такое стек и куча мы ещё поговорим позднее.

Структуры не поддерживают наследование

В отличие от классов C#, наследование структур не поддерживается, то есть вот такой код приведет к ошибке:

struct Point3DType2 : Point3D
{ }

Когда использовать структуры (struct), а когда классы (class) в C#

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

Например, использование структур вполне оправдано в примерах выше — описание точки в трехмерном пространстве. Максимум логики, которую мы можем добавить в структуру — это переопределить операторы сложения, вычитания и равенства.

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

Итого

Как и классы в C#, структуры позволяют определить пользовательский тип данных в вашем проекте. При этом, на первый взгляд, структуры (struct) практически ни чем не отличаются от классов, однако различия есть: во-первых, структуры относятся к типам значений, во-вторых, структуры не поддерживают механизмов наследования. Использовать или не использовать структуры — решение самого разработчика, однако, рекомендуется использовать тип struct только в том случае, если ваш пользовательский тип данных содержит минимум бизнес-логики или не содержит её вовсе.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии