Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Подсказка
Вы новичок в разработке программного обеспечения? Сначала начните с учебников для начинающих. Когда вам понадобятся легковесные типы значений в коде, вы столкнетесь с такими структурами.
Есть опыт на другом языке? Структуры C# — это типы значений, аналогичные структурам в C++ или Swift, но они размещаются в управляемой куче при упаковке и поддерживают интерфейсы, конструкторы и методы. Просмотрите раздел только для чтения структур для C#-специфических паттернов. Инструкции записей см. в разделе "Записи".
Структура — это тип значения, который содержит свои данные непосредственно в экземпляре, а не через ссылку на объект в heap. При назначении структуры новой переменной среда выполнения копирует весь экземпляр. Изменения в одной переменной не влияют на другую, так как каждая переменная представляет другой экземпляр. Используйте структуры для небольших упрощенных типов, основная роль которых — хранение данных, а не поведение моделирования. Примеры включают координаты, цвета, измерения или параметры конфигурации.
Объявление структуры
Определите структуру с ключевым словом struct . Структуру можно содержать поля, свойства, методы и конструкторы, как класс:
struct Point
{
public double X { get; set; }
public double Y { get; set; }
public readonly double DistanceTo(Point other)
{
var dx = X - other.X;
var dy = Y - other.Y;
return Math.Sqrt(dx * dx + dy * dy);
}
public override string ToString() => $"({X}, {Y})";
}
В Point структуре хранятся два double значения и предоставляется метод для вычисления расстояния между двумя точками. Метод DistanceTo помечен readonly , так как он не изменяет состояние структуры. Этот шаблон рассматривается в элементах чтения.
Семантика значений
Структуры — это типы значений. Назначение копирует данные, поэтому каждая переменная содержит собственную независимую копию:
var p1 = new Point { X = 3, Y = 4 };
var p2 = p1; // copies the data
p2.X = 10;
Console.WriteLine(p1); // (3, 4) — p1 is unchanged
Console.WriteLine(p2); // (10, 4) — only p2 was modified
Поскольку структуры являются контейнерами данных, назначение копирует каждый член данных в новый независимый экземпляр. Каждая копия отличается. Изменение одного не влияет на другое. Это поведение отличается от классов, где назначение копирует только ссылку, и обе переменные используют один и тот же объект. Дополнительные сведения о различиях см. в разделе "Типы значений" и ссылочные типы.
Конструкторы структуры
Конструкторы в структурах можно определить так же, как и в классах. Структуры могут иметь конструкторы без параметров , которые задают пользовательские значения по умолчанию. Термин "конструктор без параметров" отличает экземпляр, созданный с new вызовом (который выполняет логику вашего конструктора) от экземпляра по умолчанию, созданного с default помощью выражения (который инициализирует все поля нулями):
struct ConnectionSettings
{
public string Host { get; set; }
public int Port { get; set; }
public int MaxRetries { get; set; }
public ConnectionSettings()
{
Host = "localhost";
Port = 8080;
MaxRetries = 3;
}
}
Конструктор без параметров выполняется при использовании new без аргументов. Выражение default обходит конструктор и задает всем полям значения по умолчанию (0, null, false). Имейте в виду разницу:
var custom = new ConnectionSettings();
Console.WriteLine($"{custom.Host}:{custom.Port} (retries: {custom.MaxRetries})");
// localhost:8080 (retries: 3)
var defaults = default(ConnectionSettings);
Console.WriteLine($"{defaults.Host ?? "(null)"}:{defaults.Port} (retries: {defaults.MaxRetries})");
// (null):0 (retries: 0)
Компилятор автоматически инициализирует все поля, которые не заданы явным образом в конструкторе. Вы можете инициализировать только поля, которые нуждаются в значениях, отличных от значений по умолчанию:
struct GameTile
{
public int Row { get; set; }
public int Column { get; set; }
public bool IsBlocked { get; set; }
public GameTile(int row, int column)
{
Row = row;
Column = column;
// IsBlocked is automatically initialized to false
}
}
var tile = new GameTile(2, 5);
Console.WriteLine($"Tile ({tile.Row}, {tile.Column}), blocked: {tile.IsBlocked}");
// Tile (2, 5), blocked: False
Свойство IsBlocked не назначается в конструкторе, поэтому компилятор задает для него значение false (значение по умолчанию для bool). Эта функция уменьшает количество шаблонного кода в конструкторах, которые должны инициализировать только несколько полей.
Структуры только для чтения и члены только для чтения
readonly struct гарантирует, что ни один член экземпляра не изменяет состояние структуры. Компилятор применяет эту гарантию, требуя, чтобы все поля и автоматически реализованные свойства были доступны только для чтения:
readonly struct Temperature
{
public double Celsius { get; }
public Temperature(double celsius) => Celsius = celsius;
public double Fahrenheit => Celsius * 9.0 / 5.0 + 32.0;
public override string ToString() => $"{Celsius:F1}°C ({Fahrenheit:F1}°F)";
}
var temp = new Temperature(100);
Console.WriteLine(temp); // 100.0°C (212.0°F)
// temp.Celsius = 50; // Error: property is read-only
Если вам не нужна вся структуру, чтобы быть неизменяемой, пометьте отдельные члены как readonly вместо этого. Член readonly не может изменить состояние структуры, и компилятор проверяет это гарантии.
struct Velocity
{
public double X
{
readonly get;
set;
}
public double Y
{
readonly get;
set;
}
public readonly double Speed => Math.Sqrt(X * X + Y * Y);
public readonly override string ToString() => $"({X}, {Y}) speed={Speed:F2}";
}
var v = new Velocity { X = 3, Y = 4 };
Console.WriteLine(v.Speed); // 5
Console.WriteLine(v); // (3, 4) speed=5.00
v.X = 6;
Console.WriteLine(v.Speed); // 7.211...
Маркировка элементов readonly помогает компилятору оптимизировать оборонительные копии. При передаче readonly структуры в метод, принимающий in параметр, компилятор знает, что копия не требуется.
Когда следует использовать структуры
Используйте структуру, когда ваш тип:
- Представляет одно значение или небольшую группу связанных значений (примерно 16 байтов или меньше).
- Имеет семантику значений— два экземпляра с одинаковыми данными должны быть равными.
- Это в первую очередь контейнер данных, а не модель поведения.
- Не требуется наследование от базового типа (структуры не могут наследовать от других структур или классов, но они могут реализовывать интерфейсы).
Более широкое сравнение, включающее классы, записи, кортежи и интерфейсы, см. в разделе "Выбор типа".