Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Тип структуры представляет собой тип значения, который может инкапсулировать данные и связанные функции. Для определения типа структуры используется ключевое слово struct
:
public struct Coords
{
public Coords(double x, double y)
{
X = x;
Y = y;
}
public double X { get; }
public double Y { get; }
public override string ToString() => $"({X}, {Y})";
}
Сведения о ref struct
типах и readonly ref struct
типах см. в статье о типах структур ссылок .
Типы структуры имеют семантика значений. То есть переменная типа структуры содержит экземпляр этого типа. По умолчанию значения переменных копируются при назначении, передаче аргумента в метод и возврате результата метода. Для переменных структурного типа копируется экземпляр типа. Дополнительные сведения см. в разделе Типы значений.
Как правило, типы структуры используются для проектирования небольших ориентированных на данные типов, которые предоставляют минимум поведения или не предоставляют его вовсе. Например, платформа .NET использует типы структуры для представления числа (как целого, так и вещественного), логического значения, символа Юникода, экземпляра времени. Если вы сконцентрированы на поведении типа, рекомендуется определить класс. Типы классов имеют ссылочную семантику. То есть переменная типа класса содержит ссылку на экземпляр этого типа, а не сам экземпляр.
Так как типы структур имеют семантику значений, рекомендуется определить неизменяемые типы структур.
Структура readonly
Модификатор используется readonly
для объявления о том, что тип структуры неизменяем. Все элементы данных структуры readonly
должны быть доступны только для чтения:
- Объявление любого поля должно быть с модификатором
readonly
. - Любое свойство, включая автоматически реализованные, должно быть доступно только для чтения или
init
только для чтения. Установщики, доступные только для инициализации, доступны только с версии C# 9.
Это гарантирует, что ни один из элементов структуры readonly
не изменит состояние структуры. Это означает, что другие члены экземпляра, кроме конструкторов, неявно readonly
.
Примечание.
В структуре readonly
элемент данных изменяемого ссылочного типа по-прежнему может изменять свое собственное состояние. Например, вы не можете заменить экземпляр List<T>, но можете добавить в него новые элементы.
Следующий код определяет структуру readonly
с установщиками свойств, доступными только для инициализации:
public readonly struct Coords
{
public Coords(double x, double y)
{
X = x;
Y = y;
}
public double X { get; init; }
public double Y { get; init; }
public override string ToString() => $"({X}, {Y})";
}
Члены экземпляров readonly
Вы также можете использовать модификатор readonly
, чтобы объявить, что член экземпляра не изменяет состояние структуры. Если не удается объявить весь тип структуры как readonly
, используйте модификатор readonly
, чтобы пометить члены экземпляров, которые не изменяют состояние структуры.
В члене экземпляра readonly
невозможно назначать поля экземпляра структуры. Однако член readonly
может вызвать член, не являющийся readonly
. В этом случае компилятор создает копию экземпляра структуры и вызывает не-членreadonly
в этой копии. В результате исходный экземпляр структуры не изменяется.
Как правило, модификатор readonly
применяется к следующим типам элементов экземпляров.
Методы.
public readonly double Sum() { return X + Y; }
Можно также применить модификатор
readonly
к методам, переопределяющим методы, объявленные в System.Object.public readonly override string ToString() => $"({X}, {Y})";
Свойства и индексаторы.
private int counter; public int Counter { readonly get => counter; set => counter = value; }
Если необходимо применить модификатор
readonly
к методам доступа свойства или индексатора, примените его в объявлении свойства или индексатора.Примечание.
Компилятор объявляет
аксессор автоматически реализованного свойства , независимо от наличия модификатора в объявлении свойства. Модификатор
readonly
можно применить к свойству или индексатору сinit
аксессором.public readonly double X { get; init; }
Модификатор можно применить readonly
к статическим полям типа структуры, но не к другим статическим элементам, таким как свойства или методы.
Компилятор может использовать модификатор readonly
для оптимизации производительности. Дополнительные сведения см. в разделе "Избегание выделения".
Неразрушающая мутация
Выражение with
можно использовать для создания копии экземпляра типа структуры с измененными указанными свойствами и полями. Как показано в следующем примере, для указания элементов для изменения и их новых значений используется синтаксис инициализатора объектов.
public readonly struct Coords
{
public Coords(double x, double y)
{
X = x;
Y = y;
}
public double X { get; init; }
public double Y { get; init; }
public override string ToString() => $"({X}, {Y})";
}
public static void Main()
{
var p1 = new Coords(0, 0);
Console.WriteLine(p1); // output: (0, 0)
var p2 = p1 with { X = 3 };
Console.WriteLine(p2); // output: (3, 0)
var p3 = p1 with { X = 1, Y = 4 };
Console.WriteLine(p3); // output: (1, 4)
}
Структура record
Вы можете определить типы структур записей. Типы записей предоставляют встроенные функции для инкапсулирования данных. Вы можете определить оба record struct
типа и readonly record struct
типы. Структура записи не может быть ref struct
. Дополнительные сведения и примеры см. в разделе "Записи".
Встроенные массивы
Начиная с C# 12, можно объявлять встроенные массивы как тип struct
.
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBuffer
{
private char _firstElement;
}
Встроенный массив — это структура, содержащая непрерывный блок элементов N одного типа. Это эквивалент объявления фиксированного буфера в безопасном коде, который доступен только в небезопасном коде. Встроенный struct
массив имеет следующие характеристики:
- Он содержит одно поле.
- Структура не указывает явное расположение.
Кроме того, компилятор проверяет System.Runtime.CompilerServices.InlineArrayAttribute атрибут:
- Длина должна быть больше нуля (
> 0
). - Целевой тип должен быть структурой.
В большинстве случаев к встроенному массиву можно обращаться как к обычному массиву, как для чтения, так и для записи значений. Кроме того, можно использовать операторы диапазона и индекса .
Существуют минимальные ограничения на тип единственного поля в встроенном массиве. Это не может быть тип указателя:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithPointer
{
private unsafe char* _pointerElement; // CS9184
}
Но это может быть любой ссылочный тип или любой тип значения:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithReferenceType
{
private string _referenceElement;
}
Встроенные массивы можно использовать почти с любой структурой данных на C#.
Встроенные массивы — это расширенная функция языка. Они предназначены для высокопроизводительных сценариев, где встроенный смежный блок элементов быстрее, чем другие альтернативные структуры данных. Дополнительные сведения о встроенных массивах см. в разделе спецификации функции.
Инициализация структуры и значения по умолчанию
Переменная struct
типа напрямую содержит данные для этого struct
. Это создает различие между неинициализированным struct
, которое имеет значение по умолчанию, и инициализированным struct
, которое сохраняет значения, заданные при его создании. Например, рассмотрим следующий код:
public readonly struct Measurement
{
public Measurement()
{
Value = double.NaN;
Description = "Undefined";
}
public Measurement(double value, string description)
{
Value = value;
Description = description;
}
public double Value { get; init; }
public string Description { get; init; }
public override string ToString() => $"{Value} ({Description})";
}
public static void Main()
{
var m1 = new Measurement();
Console.WriteLine(m1); // output: NaN (Undefined)
var m2 = default(Measurement);
Console.WriteLine(m2); // output: 0 ()
var ms = new Measurement[2];
Console.WriteLine(string.Join(", ", ms)); // output: 0 (), 0 ()
}
Как показано в предыдущем примере, выражение значения по умолчанию игнорирует конструктор без параметров и создает значение по умолчанию типа структуры. При создании экземпляра массива типа структуры также игнорируется конструктор без параметров и создается массив, заполненный значениями по умолчанию для типа структуры.
Наиболее распространенная ситуация, когда значения по умолчанию содержатся в массивах или в других коллекциях, где внутреннее хранилище содержит блоки переменных. В следующем примере создается массив из 30 TemperatureRange
структур, каждый из которых имеет значение по умолчанию:
// All elements have default values of 0:
TemperatureRange[] lastMonth = new TemperatureRange[30];
Все поля элементов структуры должны быть определенно назначены при создании, так как struct
типы напрямую хранят свои данные. Значение default
структуры определенно назначено всем полям 0. Все поля должны быть определенно назначены при вызове конструктора. Вы инициализируете поля с помощью следующих механизмов:
- Вы можете добавить инициализаторы полей в любое поле или автоматически реализованное свойство.
- Вы можете инициализировать любые поля или автоматические свойства в теле конструктора.
Начиная с C# 11, если вы не инициализируете все поля в структуре, компилятор добавляет код в конструктор, который инициализирует эти поля в значение по умолчанию. Структура, которой присваивается значение default
, инициализируется шаблоном битов, состоящим из нулей. Структура, инициализируемая с помощью new
, инициализирована в 0-разрядном шаблоне, а затем выполняется любой инициализатор полей и конструктор.
public readonly struct Measurement
{
public Measurement(double value)
{
Value = value;
}
public Measurement(double value, string description)
{
Value = value;
Description = description;
}
public Measurement(string description)
{
Description = description;
}
public double Value { get; init; }
public string Description { get; init; } = "Ordinary measurement";
public override string ToString() => $"{Value} ({Description})";
}
public static void Main()
{
var m1 = new Measurement(5);
Console.WriteLine(m1); // output: 5 (Ordinary measurement)
var m2 = new Measurement();
Console.WriteLine(m2); // output: 0 ()
var m3 = default(Measurement);
Console.WriteLine(m3); // output: 0 ()
}
Каждый struct
имеет public
беспараметрический конструктор. При написании конструктора без параметров он должен быть открытым. Если структура объявляет инициализаторы полей, она должна явно объявить конструктор. Этот конструктор не обязательно должен быть без параметров. Если структура объявляет инициализатор полей, но никаких конструкторов, компилятор сообщает об ошибке. Любой явно объявленный конструктор (с параметрами или без параметров) выполняет все инициализаторы полей для этой структуры. Все поля без инициализатора полей или назначения в конструкторе задаются значением по умолчанию. Дополнительные сведения см. в примечании к предложению новой возможности в разделе Конструкторы структур без параметров.
Начиная с C# 12 struct
типы могут определять основной конструктор в рамках объявления. Первичные конструкторы предоставляют лаконичный синтаксис для параметров конструктора, которые могут использоваться в теле struct
, в любом объявлении члена этой структуры.
Если все поля экземпляра типа структуры доступны, можно обойтись без оператора new
для его создания. В этом случае необходимо инициализировать все поля экземпляра перед первым использованием экземпляра. Следующий пример показывает, как это сделать:
public static class StructWithoutNew
{
public struct Coords
{
public double x;
public double y;
}
public static void Main()
{
Coords p;
p.x = 3;
p.y = 4;
Console.WriteLine($"({p.x}, {p.y})"); // output: (3, 4)
}
}
Для встроенных типов значенийиспользуйте соответствующие литералы, чтобы указать значение типа.
Ограничения при проектировании типа структуры
Структуры имеют большую часть возможностей типа класса . Существуют некоторые исключения:
- Тип структуры не может наследовать от другого типа класса или структуры и не может быть базовым для класса. Однако тип структуры может реализовывать интерфейсы.
- Вы не можете объявить метод завершения в типе структуры.
- До версии C# 11 конструктор типа структуры должен был инициализировать все поля экземпляра.
Передача переменных типа структуры по ссылке
При передаче переменной типа структуры в метод в качестве аргумента или возврате значения типа структуры из метода копируется весь экземпляр типа структуры. Передача по значению может повлиять на производительность кода в сценариях, требующих высокой производительности, которые включают большие типы структур. Копирования значений можно избежать, передав переменную типа структуры по ссылке.
ref
Используйте модификаторы параметров метода , out
in
ref readonly
чтобы указать, что аргумент должен передаваться по ссылке. Чтобы возвратить результат метода по ссылке, используйте ref returns. Дополнительные сведения см. в разделе Избегайте выделений.
ограничение struct
Вы можете использовать ключевое слово struct
в ограничении struct
, чтобы указать, что параметр типа является типом значения, не допускающим значения NULL. Типы структуры и перечисления удовлетворяют ограничению struct
.
Преобразования
Для любого типа структуры (за исключением ref struct
типов) существуют преобразования упаковки и распаковки в System.ValueType и из System.Object типов. Существуют также преобразования упаковки и распаковки между типом структуры и интерфейсом, который он реализует.
Спецификация языка C#
Дополнительные сведения см. в разделе Структуры в спецификации языка C#.
Дополнительные сведения о struct
функциях см. в следующих заметках о предлагаемых функциях:
- Структуры только для чтения
- Члены экземпляра, доступные только для чтения
- безпараметрические конструкторы структур
-
Разрешить выражение
with
для структур - структуры записей
- автоматические структуры по умолчанию