Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Заметка
Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Она включает предлагаемые изменения спецификации, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.
Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия фиксируются в соответствующих записях о проектировании языка (LDM) на собраниях .
Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .
Проблема чемпиона: https://github.com/dotnet/csharplang/issues/99
Сводка
Поддержка конструкторов без параметров и инициализаторов полей экземпляра для типов структур.
Мотивация
Явные конструкторы без параметров дают больше контроля над минимально созданными экземплярами типа структуры.
Инициализаторы полей экземпляра позволяют упростить процесс инициализации в нескольких конструкторах.
Вместе они закроют очевидный разрыв между объявлениями struct
и class
.
Поддержка инициализаторов полей также позволяет инициализации полей в объявлениях record struct
без явной реализации основного конструктора.
record struct Person(string Name)
{
public object Id { get; init; } = GetNextId();
}
Если инициализаторы полей структуры поддерживаются для конструкторов с параметрами, естественно распространить эту поддержку и на конструкторы без параметров.
record struct Person()
{
public string Name { get; init; }
public object Id { get; init; } = GetNextId();
}
Предложение
Инициализаторы полей экземпляра
Объявления полей экземпляра для структуры могут включать инициализаторы.
Как и при инициализаторах полей класса §15.5.6.3:
Инициализатор переменных для поля экземпляра не может ссылаться на созданный экземпляр.
Сообщается об ошибке, если у структуры есть инициализаторы полей и не объявленные конструкторы экземпляров, так как инициализаторы полей не будут выполняться.
struct S { int F = 42; } // error: 'struct' with field initializers must include an explicitly declared constructor
Конструкторы
Структура может объявлять конструктор экземпляра без параметров.
Конструктор экземпляра без параметров действителен для всех типов структур, включая struct
, readonly struct
, ref struct
и record struct
.
Если конструктор экземпляра без параметров не объявлен, структура (см. §16.4.9) ...
Неявно имеет конструктор экземпляра без параметров, который всегда возвращает значение, которое приводит к настройке всех полей типа значения по умолчанию и всех полей ссылочного типа значение NULL.
Модификаторы
Конструктор структуры без параметров экземпляра должен быть объявлен public
.
struct S0 { } // ok
struct S1 { public S1() { } } // ok
struct S2 { internal S2() { } } // error: parameterless constructor must be 'public'
Недоступные конструкторы игнорируются при импорте типов из метаданных.
Конструкторы можно объявить extern
или unsafe
.
Конструкторы не могут быть partial
.
Выполнение инициализаторов полей
Инициализаторы переменных экземпляров (§15.11.3) изменены следующим образом:
Если конструктор экземпляра класса не имеет инициализатора конструктора или имеет инициализатор конструктора формы
base(...)
, то этот конструктор неявно выполняет инициализации, указанные инициализаторами переменных полей экземпляра, объявленных в его классе. Это соответствует последовательности назначений, которые выполняются сразу после входа в конструктор и перед неявным вызовом конструктора прямого базового класса.Если конструктор экземпляра структуры не имеет инициализатора конструктора, конструктор неявно выполняет инициализации, указанные variable_initializerполя экземпляра, объявленные в его структуре. Это соответствует последовательности назначений, которые выполняются сразу после входа в конструктор.
Когда конструктор экземпляра структуры имеет инициализатор конструктора
this()
, представляющий конструктор по умолчанию без параметров, объявленный конструктор неявно обнуляет все поля экземпляра и производит инициализацию, заданную параметрами variable_initializerдля полей экземпляра, объявленных в его структуре. Сразу после входа в конструктор все поля типа значения задаются по умолчанию, а для всех полей ссылочного типа задано значениеnull
. Сразу после этого выполняется последовательность назначений, соответствующих инициализаторам переменных в форме variable_initializer.
Определенное назначение
Поля экземпляров (кроме полей fixed
) должны быть определенно назначены в конструкторах экземпляров структуры, которые не имеют инициализатора this()
(см. §16.4.9).
struct S0 // ok
{
int x;
object y;
}
struct S1 // error: 'struct' with field initializers must include an explicitly declared constructor
{
int x = 1;
object y;
}
struct S2
{
int x = 1;
object y;
public S2() { } // error in C# 10 (valid starting in C# 11): field 'y' must be assigned
}
struct S3 // ok
{
int x = 1;
object y;
public S3() { y = 2; }
}
Нет инициализатора base()
Инициализатор base()
запрещен в конструкторах структур.
Компилятор не будет вызывать базовый конструктор System.ValueType
из конструкторов экземпляров структуры.
record struct
Сообщается об ошибке, если record struct
имеет инициализаторы полей и не содержит основного конструктора или конструкторов экземпляров, так как инициализаторы полей не будут выполняться.
record struct R0; // ok
record struct R1 { int F = 42; } // error: 'struct' with field initializers must include an explicitly declared constructor
record struct R2() { int F = 42; } // ok
record struct R3(int F); // ok
record struct
с пустым списком параметров будет иметь первичный конструктор без параметров.
record struct R3(); // primary .ctor: public R3() { }
record struct R4() { int F = 42; } // primary .ctor: public R4() { F = 42; }
Явный конструктор без параметров в record struct
должен иметь инициализатор this
, который вызывает основной конструктор или явно объявленный конструктор.
record struct R5(int F)
{
public R5() { } // error: must have 'this' initializer that calls explicit .ctor
public R5(object o) : this() { } // ok
public int F = F;
}
Поля
Неявно определенный конструктор без параметров будет обнулять поля, а не вызывать конструкторы без параметров для типов полей. Сообщения о предупреждениях, что конструкторы полей игнорируются, отсутствуют. Нет изменений с C#9.
struct S0
{
public S0() { }
}
struct S1
{
S0 F; // S0 constructor ignored
}
struct S<T> where T : struct
{
T F; // constructor (if any) ignored
}
выражение default
default
игнорирует конструктор без параметров и создает экземпляр с обнулёнными полями.
Нет изменений с C#9.
// struct S { public S() { } }
_ = default(S); // constructor ignored, no warning
new()
При создании объекта вызывается конструктор без параметров, если он открыт; в противном случае экземпляр равен нулю. Нет изменений с C#9.
// public struct PublicConstructor { public PublicConstructor() { } }
// public struct PrivateConstructor { private PrivateConstructor() { } }
_ = new PublicConstructor(); // call PublicConstructor::.ctor()
_ = new PrivateConstructor(); // initobj PrivateConstructor
Волна предупреждений может сообщить предупреждение об использовании new()
с типом структуры с конструкторами, но без конструктора без параметров.
Предупреждение не будет сообщено при использовании замены такого типа структуры для параметра типа с ограничением new()
или struct
.
struct S { public S(int i) { } }
static T CreateNew<T>() where T : new() => new T();
_ = new S(); // no warning called
_ = CreateNew<S>(); // ok
Неинициализированные значения
Локальная переменная или поле структурного типа, которое не инициализировано явно, обнуляется. Компилятор сообщает определенную ошибку назначения для неинициализированной структуры, которая не пуста. Нет изменений с C#9.
NoConstructor s1;
PublicConstructor s2;
s1.ToString(); // error: use of unassigned local (unless type is empty)
s2.ToString(); // error: use of unassigned local (unless type is empty)
Выделение массива
Выделение массива игнорирует любой конструктор без параметров и создает ноль элементов. Нет изменений с C#9.
// struct S { public S() { } }
var a = new S[1]; // constructor ignored, no warning
Значение параметра по умолчанию new()
Значение параметра по умолчанию new()
привязывается к конструктору без параметров, если общедоступный (и сообщает об ошибке, что значение не является константой); в противном случае экземпляр равен нулю.
Нет изменений с C#9.
// public struct PublicConstructor { public PublicConstructor() { } }
// public struct PrivateConstructor { private PrivateConstructor() { } }
static void F1(PublicConstructor s1 = new()) { } // error: default value must be constant
static void F2(PrivateConstructor s2 = new()) { } // ok: initobj
Ограничения параметров типа: new()
и struct
Ограничения параметров типа new()
и struct
требуют, чтобы конструктор без параметров был public
, если он определен (см. ограничения соответствия — §8.4.5).
Компилятор предполагает, что все структуры удовлетворяют ограничениям new()
и struct
.
Нет изменений с C#9.
// public struct PublicConstructor { public PublicConstructor() { } }
// public struct InternalConstructor { internal InternalConstructor() { } }
static T CreateNew<T>() where T : new() => new T();
static T CreateStruct<T>() where T : struct => new T();
_ = CreateNew<PublicConstructor>(); // ok
_ = CreateStruct<PublicConstructor>(); // ok
_ = CreateNew<InternalConstructor>(); // compiles; may fail at runtime
_ = CreateStruct<InternalConstructor>(); // compiles; may fail at runtime
new T()
генерируется как вызов System.Activator.CreateInstance<T>()
, и компилятор предполагает, что реализация CreateInstance<T>()
вызывает конструктор без параметров public
, если он определен.
в .NET Framework Activator.CreateInstance<T>()
вызывает конструктор без параметров, если ограничение where T : new()
, но, как представляется, игнорирует конструктор без параметров, если ограничение where T : struct
.
Необязательные параметры
Конструкторы с необязательными параметрами не считаются конструкторами без параметров. Нет изменений с C#9.
struct S1 { public S1(string s = "") { } }
struct S2 { public S2(params object[] args) { } }
_ = new S1(); // ok: ignores constructor
_ = new S2(); // ok: ignores constructor
Метаданные
Явные безпараметрические конструкторы структур будут создаваться в метаданных.
Открытые конструкторы экземпляров без параметров будут импортированы из метаданных; не являющиеся открытыми конструкторы экземпляров будут игнорироваться. Нет изменений с C#9.
См. также
Совещания по дизайну
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-04-28.md#open-questions-in-record-and-parameterless-structs
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-03-10.md#parameterless-struct-constructors
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-01-27.md#field-initializers
C# feature specifications