Структуры

Последнее обновление: 01.12.2023

Наряду с классами структуры представляют еще один способ создания собственных типов данных в C#. Более того многие примитивные типы, например, int, double и т.д., по сути являются структурами.

Определение структуры

Для определения структуры применяется ключевое слово struct:

struct имя_структуры
{
	// элементы структуры
}

После слова struct идет название структуры и далее в фигурных скобках размещаются элементы структуры - поля, методы и т.д.

Например, определим структуру, которая будет называться Person и которая будет представлять человека:

struct Person
{
}

Начиная с версии C# 12, если структура имеет пустое определение (не содержат полей, свойств, методов), то фигурные скобки после названия типа можно не использовать:

struct Person;

Как и классы, структуры могут хранить состояние в виде полей (переменных) и определять поведение в виде методов. Например, добавим в структуру Person пару полей и метод:

struct Person
{
    public string name;
    public int age;

    public void Print()
    {
        Console.WriteLine($"Имя: {name}  Возраст: {age}");
    }
}

В данном случае определены две переменные - name и age для хранения соответственно имени и возраста человека и метод Print для вывода информации о человеке на консоль.

И как и в случае с классами, для обращения к функциональности структуры - полям, методам и другим компонентам структуры применяется точечная нотация - после объекта структуры ставится точка, а затем указывается компонент структуры:

объект.поле_структуры
объект.метод_структуры(параметры_метода)

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

Инициализация с помощью конструктора

Для использования структуры ее необходмо инициализировать. Для инициализации создания объектов структуры, как и в случае с классами, применяется вызов конструктура с оператором new. Даже если в коде стуктуры не определено ни одного конструктора, тем не менее имеет как минимум один конструктор - конструктор по умолчанию, который генерируется компилятором. Этот конструктор не принимает параметров и создает объект структуры со значениями по умолчанию.

new название_структуры();

Например, создадим объект структуры Person с помощью конструктора по умолчанию:

Person tom = new Person();	// вызов конструктора
// или так 
// Person tom = new();

tom.name = "Tom";	// изменяем значение по умолчанию в поле name

tom.Print();    // Имя: Tom  Возраст: 0

struct Person
{
    public string name;
    public int age;

    public void Print()
    {
        Console.WriteLine($"Имя: {name}  Возраст: {age}");
    }
}

В данном случае создается объект tom. Для его создания вызывается конструктор по умолчанию, который устанавливает значения по умолчанию для его полей. Для числовых данных это значение 0, поэтому поле age будет иметь значение 0. Для строк это значение null, которое указывает на отсутствие значения. Но далее, если поля доступны (а в данном случае поскольку они имеют модификатор public они доступны), мы можем изменить их значения. Так, здесь полю name присваивается строка "Tom". Соответственно при выполнении метода Print() мы получим следующий консольный вывод:

Имя: Tom  Возраст: 0

Непосредственная иницилизация полей

Если все поля структуры доступны (как в случае с полями структуры Person, который имеют модификатор public), то структуру можно инициализировать без вызова конструктора. В этом случае необходимо присвоить значения всем полям структуры перед получением значений полей и обращением к методам структуры. Например:

Person tom;			// не вызываем конструктор
// инициализация полей
tom.name = "Sam";
tom.age = 37;

tom.Print();    // Имя: Sam  Возраст: 37

struct Person
{
    public string name;
    public int age;

    public void Print()
    {
        Console.WriteLine($"Имя: {name}  Возраст: {age}");
    }
}

Инициализация полей по умолчанию

Стоит отметить, что начиная с версии C# 10, мы можем напрямую инициализировать поля структуры при их определении (до C# 10 это делать было нельзя):

Person tom = new Person();
tom.Print();    // Имя:Tom  Возраст: 1

struct Person
{
	// инициализация полей значениями по умолчанию - доступна с C#10
    public string name = "Tom";
    public int age = 1;
    public Person() { }
    public void Print() =>Console.WriteLine($"Имя: {name}  Возраст: {age}");
}

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

Конструкторы структуры

Как и класс, структура может определять конструкторы. Например, добавим в структуру Person конструктор:

Person tom = new();
Person bob = new("Bob");
Person sam = new("Sam", 25);

tom.Print();    // !!!! Имя:   Возраст: 0
bob.Print();    // Имя: Bob  Возраст: 1 
sam.Print();    // Имя: Sam  Возраст: 25

struct Person
{
    public string name;
    public int age;

    public Person(string name = "Tom", int age = 1)
    {
        this.name = name;
        this.age = age;
    }
    public void Print() => Console.WriteLine($"Имя: {name}  Возраст: {age}");
}

В данном случае в структуре Person определен конструктор с двумя параметрами, для которых предоставлены значения по умолчания. Однако обратите внимание на создание первого объекта структуры:

Person tom = new();	// по прежнему используется конструктор без параметров по умолчанию
tom.Print();    // !!!! Имя:   Возраст: 0

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

Однако начиная с версии C# 10 мы можем определить свой конструктор без параметров:

Person tom = new();

tom.Print();    // Имя: Tom  Возраст: 37

struct Person
{
    public string name;
    public int age;

    public Person()
    {
        name = "Tom";
        age = 37;
    }
    public void Print() => Console.WriteLine($"Имя: {name}  Возраст: {age}");
}

Стоит отметить, что до версии C# 11 при определении конструктора структуру в нем необходимо было инициализировать все поля структуры, начиная с версии C# 11 это делать необязательно.

В случае если нам необходимо вызывать конструкторы с различным количеством параметров, то мы можем, как и в случае с классами, вызывать их по цепочке:

Person tom = new();
Person bob = new("Bob");
Person sam = new("Sam", 25);

tom.Print();    // Имя: Tom  Возраст: 1
bob.Print();    // Имя: Bob  Возраст: 1 
sam.Print();    // Имя: Sam  Возраст: 25

struct Person
{
    public string name;
    public int age;

    public Person() : this("Tom")
    { }
    public Person(string name) : this(name, 1)
    { }
    public Person(string name, int age)
    {
        this.name = name;
        this.age = age;
    }
    public void Print() => Console.WriteLine($"Имя: {name}  Возраст: {age}");
}

Консольный вывод программы:

Имя: Tom  Возраст: 1
Имя: Bob  Возраст: 1 
Имя: Sam  Возраст: 25

Начиная с версии C# 12 для структур, как и для классов, можно определять первичные конструкторы. Первичные конструкторы позволяют добавлять параметры к определению класса/структуры и использовать эти параметры внутри класса/структуры:

var tom = new Person("Tom", 38);
tom.Print();

public struct Person(string name, int age)
{
    public Person(string name) : this(name, 18) { }
    public void Print() => Console.WriteLine($"name: {name}, age: {age}");
}

Здесь для структуры Person определен первичный конструктор с двумя параметрами - name и age. За кадром для каждого параметра первичного конструктора в классе создается приватное поле, которое хранит значение параметра. Благодаря этому они могут использоваться в теле класса.

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

public Person(string name) : this(name, 18) { }

Инициализатор структуры

Также, как и для класса, можно использовать инициализатор для создания структуры:

Person tom = new Person { name = "Tom", age = 22 };

tom.Print();    // Имя: Tom  Возраст: 22

struct Person
{
    public string name;
    public int age;
    public void Print() => Console.WriteLine($"Имя: {name}  Возраст: {age}");
}

При использовании инициализатора сначала вызывается конструктор без параметров: если мы явным образом не определили конструктор без параметров, то вызывается конструктор по умолчанию. А затем его полям присваиваются соответствующие значения.

Копирование структуры с помощью with

Если нам необходимо скопировать в один объект структуры значения из другого с небольшими изменениями, то мы можем использовать оператор with:

Person tom = new Person { name = "Tom", age = 22 };
Person bob = tom with { name = "Bob" };
bob.Print();    // Имя: Bob  Возраст: 22

В данном случае объект bob получает все значения объекта tom, а затем после оператора with в фигурных скобках указывается поля со значениями, которые мы хотим изменить.

Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850