Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Подсказка
Вы новичок в разработке программного обеспечения? Сначала начните с учебников для начинающих. Когда вам понадобятся легковесные типы значений в коде, вы столкнетесь с такими структурами.
Есть опыт на другом языке? Структуры 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 байтов или меньше).
- Имеет семантику значений— два экземпляра с одинаковыми данными должны быть равными.
- Это в первую очередь контейнер данных, а не модель поведения.
- Не требуется наследование от базового типа (структуры не могут наследовать от других структур или классов, но они могут реализовывать интерфейсы).
Более широкое сравнение, включающее классы, записи, кортежи и интерфейсы, см. в разделе "Выбор типа".