- Типы структур (справочник по C#)
- Структура readonly
- Члены экземпляров readonly
- Обратимое изменение
- Структура record
- Встроенные массивы
- Инициализация структуры и значения по умолчанию
- Ограничения при проектировании типа структуры
- Передача переменных типа структуры по ссылке
- Ограничение struct
- Преобразования
- Спецификация языка C#
- См. также раздел
Типы структур (справочник по C#)
Тип структуры представляет собой тип значения, который может инкапсулировать данные и связанные функции. Для определения типа структуры используется ключевое слово 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() => $"(, )"; >
Дополнительные сведения о ref struct типах и readonly ref struct см. в статье Типы ссылок на структуры .
Типы структуры имеют семантики значений. То есть переменная типа структуры содержит экземпляр этого типа. По умолчанию значения переменных копируются при назначении, передаче аргумента в метод и возврате результата метода. Для переменных типа структуры копируется экземпляр типа . Дополнительные сведения см. в разделе Типы значений.
Как правило, типы структуры используются для проектирования небольших ориентированных на данные типов, которые предоставляют минимум поведения или не предоставляют его вовсе. Например, платформа .NET использует типы структуры для представления числа (как целого, так и вещественного), логического значения, символа Юникода, экземпляра времени. Если вы сконцентрированы на поведении типа, рекомендуется определить класс. Типы классов имеют семантики ссылок. То есть переменная типа класса содержит ссылку на экземпляр этого типа, а не сам экземпляр.
Так как типы структур имеют семантику значений, рекомендуется определить неизменяемые типы структур.
Структура readonly
Модификатор readonly используется для объявления типа структуры неизменяемым. Все элементы данных структуры readonly должны быть доступны только для чтения:
- Любое объявление поля должно иметь readonly модификатор.
- Все свойства, включая автоматические реализованные, должны быть доступны только для чтения. В C# 9.0 и более поздних версий свойство может иметь метод доступа init .
Это гарантирует, что ни один из элементов структуры readonly не изменит состояние структуры. Это означает, что другие члены экземпляра, кроме конструкторов, неявно readonly являются .
В структуре readonly элемент данных изменяемого ссылочного типа по-прежнему может изменять свое собственное состояние. Например, вы не можете заменить экземпляр List , но можете добавить в него новые элементы.
В следующем коде определяется структура readonly с методами задания свойств только для инициализации, которые доступны в C# 9.0 и более поздних версий:
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() => $"(, )"; >
Члены экземпляров readonly
Модификатор readonly также можно использовать для объявления того, что член экземпляра не изменяет состояние структуры. Если не удается объявить весь тип структуры как readonly , используйте модификатор readonly , чтобы пометить члены экземпляров, которые не изменяют состояние структуры.
В члене экземпляра readonly невозможно назначать поля экземпляра структуры. Однако член readonly может вызвать член, не являющийся readonly . В этом случае компилятор создает копию экземпляра структуры и вызывает не-член readonly в этой копии. В результате исходный экземпляр структуры не изменяется.
Как правило, модификатор readonly применяется к следующим типам элементов экземпляров.
public readonly double Sum()
Можно также применить модификатор readonly к методам, переопределяющим методы, объявленные в System.Object.
public readonly override string ToString() => $"(, )";
private int counter; public int Counter < readonly get =>counter; set => counter = value; >
Если необходимо применить модификатор readonly к методам доступа свойства или индексатора, примените его в объявлении свойства или индексатора.
Примечание Компилятор объявляет метод доступа get автоматически реализуемого свойства как readonly независимо от наличия модификатора readonly в объявлении свойства.
В C# 9.0 и более поздних версий вы можете применить модификатор readonly к свойству или индексатору с помощью метода доступа init :
Модификатор readonly можно применить к статическим полям типа структуры, но не к другим статическим членам, таким как свойства или методы.
Компилятор может использовать модификатор readonly для оптимизации производительности. Дополнительные сведения см. в разделе Предотвращение выделения ресурсов.
Обратимое изменение
Начиная с C# 10 можно использовать выражение 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() => $"(, )"; > 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
Начиная с C# 10, можно определить типы структуры записей. Типы записей предоставляют встроенные функции для инкапсуляции данных. Можно определить record struct типы и readonly record struct . Структурой записей не может быть ref struct . Дополнительные сведения и примеры см. в разделе Записи.
Встроенные массивы
Начиная с C# 12, встроенные массивы можно объявлять как тип struct :
[System.Runtime.CompilerServices.InlineArray(10)] public struct CharBuffer
Встроенный массив — это структура, содержащая непрерывный блок N элементов одного типа. Это безопасный эквивалент объявления фиксированного буфера , доступного только в небезопасном коде. Встроенный массив имеет struct следующие характеристики:
В большинстве случаев доступ к встроенному массиву можно получить как к массиву для чтения и записи значений. Кроме того, можно использовать операторы диапазона и индекса .
Существуют минимальные ограничения на тип одного поля. Это не может быть тип указателя, но это может быть любой ссылочный тип или любой тип значения. Встроенные массивы можно использовать практически с любой структурой данных 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() => $" ()"; > 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. Все поля должны быть определенно назначены при вызове конструктора. Поля инициализируются с помощью следующих механизмов:
- Вы можете добавить инициализаторы полей в любое поле или автоматически реализованное свойство.
- В тексте конструктора можно инициализировать любые поля или свойства auto.
Начиная с C# 11, если не инициализировать все поля в структуре, компилятор добавляет код в конструктор, который инициализирует эти поля значением по умолчанию. Компилятор выполняет обычный анализ определенных назначений. Всем полям, которые были доступны до назначения или которые не были назначены по завершении выполнения конструктором, присваиваются значения по умолчанию до выполнения тела конструктора. Если this доступ к объекту осуществляется до назначения всех полей, то перед выполнением тела конструктора инициализируется значением по умолчанию.
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() => $" ()"; > 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($"(, )"); // output: (3, 4) > >
В случае встроенных типов значения используйте соответствующие литералы, чтобы указать значение типа.
Ограничения при проектировании типа структуры
Структуры имеют большую часть возможностей типа класса . Существуют некоторые исключения и исключения, которые были удалены в более поздних версиях:
- Тип структуры не может наследовать от другого типа класса или структуры и не может быть базовым для класса. Однако тип структуры может реализовывать интерфейсы.
- Вы не можете объявить метод завершения в типе структуры.
- До C# 11 конструктор типа структуры должен инициализировать все поля экземпляра типа .
- До C# 10 нельзя объявлять конструктор без параметров.
- До C# 10 нельзя инициализировать поле или свойство экземпляра при объявлении.
Передача переменных типа структуры по ссылке
При передаче переменной типа структуры в метод в качестве аргумента или возврате значения типа структуры из метода копируется весь экземпляр типа структуры. Передача по значению может повлиять на производительность кода в сценариях с высокой производительностью, в которых используются большие типы структур. Копирования значений можно избежать, передав переменную типа структуры по ссылке. Используйте модификаторы параметра метода ref , out или in , чтобы указать, что аргумент должен передаваться по ссылке. Чтобы возвратить результат метода по ссылке, используйте ref returns. Дополнительные сведения см. в разделе Предотвращение выделения ресурсов.
Ограничение struct
Вы можете использовать ключевое слово struct в ограничении struct , чтобы указать, что параметр типа является типом значения, не допускающим значения NULL. Типы структуры и перечисления удовлетворяют ограничению struct .
Преобразования
Для любого типа структуры (кроме ref struct типов) существуют преобразования boxing и unboxing в System.ValueType типы и System.Object . Существуют упаковка-преобразование и распаковка-преобразование между типом структуры и любым интерфейсом, который он реализует.
Спецификация языка C#
Дополнительные сведения см. в разделе Структуры в спецификации языка C#.
Дополнительные сведения о struct функциях см. в следующих примечаниях к предложениям функций: