Konstruktory struktury bez parametrów
Notatka
Ten artykuł jest specyfikacją funkcji. Specyfikacja służy jako dokument projektowy dla funkcji. Zawiera proponowane zmiany specyfikacji wraz z informacjami wymaganymi podczas projektowania i opracowywania funkcji. Te artykuły są publikowane do momentu sfinalizowania proponowanych zmian specyfikacji i włączenia ich do obecnej specyfikacji ECMA.
Mogą wystąpić pewne rozbieżności między specyfikacją funkcji a ukończoną implementacją. Te różnice są przechwytywane w odpowiednich spotkania projektowego języka (LDM).
Więcej informacji na temat procesu wdrażania specyfikacji funkcji można znaleźć w standardzie języka C# w artykule dotyczącym specyfikacji .
Streszczenie
Obsługa konstruktorów bez parametrów i inicjatorów pól wystąpień dla typów struktur.
Motywacja
Jawne konstruktory bez parametrów zapewniają większą kontrolę nad minimalnie skonstruowanymi wystąpieniami typu struktury.
Inicjalizatory pól instancji umożliwiłyby uproszczone inicjowanie w wielu konstruktorach.
Razem zamkną one oczywistą różnicę między deklaracjami struct
i class
.
Obsługa inicjatorów pól umożliwia również inicjowanie pól w deklaracjach record struct
bez jawnego implementowania konstruktora podstawowego.
record struct Person(string Name)
{
public object Id { get; init; } = GetNextId();
}
Jeśli inicjatory pól struktury są obsługiwane dla konstruktorów z parametrami, wydaje się naturalne, że można je rozszerzyć również na konstruktory bez parametrów.
record struct Person()
{
public string Name { get; init; }
public object Id { get; init; } = GetNextId();
}
Propozycja
Inicjatory pól wystąpienia
Deklaracje pól wystąpienia dla struktury mogą zawierać inicjalizatory.
Podobnie jak w przypadku inicjatorów pól klasy §15.5.6.3:
Inicjator zmiennej dla pola wystąpienia nie może odwoływać się do tworzonego wystąpienia.
Zgłaszany jest błąd, jeśli struktura ma inicjatory pól, a nie ma zadeklarowanych konstruktorów wystąpień, ponieważ wtedy inicjatory pól nie zostaną uruchomione.
struct S { int F = 42; } // error: 'struct' with field initializers must include an explicitly declared constructor
Konstruktorzy
Struktura może zadeklarować konstruktor instancji bez parametrów.
Konstruktor wystąpienia bez parametrów jest prawidłowy dla wszystkich rodzajów struktur, w tym struct
, readonly struct
, ref struct
i record struct
.
Jeżeli nie zadeklarowano konstruktora instancji bez parametrów, struktura (zobacz §16.4.9) ...
Niejawnie ma konstruktor instancji bez parametrów, który zawsze zwraca wartość powstałą przez ustawienie wszystkich pól typu wartości na ich domyślną wartość. Wszystkie pola typu odwołania są ustawione na null.
Modyfikatory
Konstruktor struktury wystąpienia bez parametrów musi być zadeklarowany public
.
struct S0 { } // ok
struct S1 { public S1() { } } // ok
struct S2 { internal S2() { } } // error: parameterless constructor must be 'public'
Konstruktory inne niż publiczne są ignorowane podczas importowania typów z metadanych.
Konstruktory można zadeklarować extern
lub unsafe
.
Konstruktorów nie można używać jako partial
.
Wykonywanie inicjatorów pól
inicjalizatory zmiennych instancji (§15.11.3) są zmodyfikowane w następujący sposób:
Gdy konstruktora klasy wystąpienia nie ma inicjatora konstruktora lub ma inicjator konstruktora formularza
base(...)
, ten konstruktor niejawnie wykonuje inicjalizacji określone przez variable_initializers pól wystąpienia zadeklarowanych w swojej klasie. Odpowiada to sekwencji przypisań, które są wykonywane natychmiast po wejściu do konstruktora i przed niejawnym wywołaniem bezpośredniego konstruktora klasy bazowej.Gdy konstruktor instancji struktury nie ma inicjatora konstruktora, ten konstruktor niejawnie wykonuje inicjalizacje określone przez variable_initializerpól instancji zadeklarowanych w jego strukturze. Koreluje to z sekwencją przypisań, które są wykonywane natychmiast po wejściu do konstruktora.
Gdy konstruktor wystąpienia struktury ma inicjator konstruktora
this()
, który reprezentuje domyślny konstruktor bez parametrów, zadeklarowany konstruktor niejawnie czyści wszystkie pola wystąpienia i wykonuje inicjalizacje określone przez variable_initializerpól wystąpienia zadeklarowanych w jego strukturze. Natychmiast po wpisie do konstruktora wszystkie pola typu wartości są ustawione na ich wartość domyślną, a wszystkie pola typu odwołania są ustawione nanull
. Natychmiast po tym jest wykonywana sekwencja przypisań odpowiadających variable_initializers.
Konkretne przypisanie
Pola wystąpienia (inne niż pola fixed
) muszą być zdecydowanie przypisane w konstruktorach wystąpienia struktury, które nie mają inicjatora this()
(zobacz §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; }
}
Brak inicjatora base()
Inicjalizator base()
jest niedozwolony w konstruktorach struktury.
Kompilator nie generuje wywołania konstruktora bazowego System.ValueType
z konstruktorów instancji struktury.
record struct
Zgłaszany jest błąd, jeśli record struct
zawiera inicjatory pól i nie zawiera konstruktora podstawowego ani żadnych konstruktorów wystąpień, ponieważ inicjatory pól nie zostaną uruchomione.
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
z pustą listą parametrów będzie miał konstruktor główny bez parametrów.
record struct R3(); // primary .ctor: public R3() { }
record struct R4() { int F = 42; } // primary .ctor: public R4() { F = 42; }
Konstruktor jawny bez parametrów w record struct
musi mieć inicjator this
, który wywołuje konstruktor główny lub konstruktor jawnie zadeklarowany.
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;
}
Pola
Niejawnie zdefiniowany konstruktor bez parametrów wyzeruje pola zamiast wywoływać jakiekolwiek konstruktory bez parametrów dla typów pól. Nie zgłoszono żadnych ostrzeżeń, że konstruktory pól są ignorowane. Brak zmian od 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
}
wyrażenie default
default
ignoruje konstruktor bez parametrów i generuje zerowane wystąpienie.
Brak zmian od C#9.
// struct S { public S() { } }
_ = default(S); // constructor ignored, no warning
new()
Tworzenie obiektu wywołuje konstruktor bez parametrów, jeśli jest publiczny; w przeciwnym razie wystąpienie ma wartość zero. Brak zmian w stosunku do C#9.
// public struct PublicConstructor { public PublicConstructor() { } }
// public struct PrivateConstructor { private PrivateConstructor() { } }
_ = new PublicConstructor(); // call PublicConstructor::.ctor()
_ = new PrivateConstructor(); // initobj PrivateConstructor
Fala ostrzegawcza może zgłosić ostrzeżenie dotyczące użycia new()
z typem struktury, który ma konstruktory, ale nie konstruktor bez parametrów.
Podczas używania zastępowania takiego typu struktury dla parametru typu z ograniczeniem new()
lub struct
nie zostanie zgłoszone żadne ostrzeżenie.
struct S { public S(int i) { } }
static T CreateNew<T>() where T : new() => new T();
_ = new S(); // no warning called
_ = CreateNew<S>(); // ok
Niezainicjowane wartości
Lokalna zmienna lub pole typu struktury, które nie jest jawnie zainicjowane, jest wyzerowane. Kompilator zgłasza określony błąd przypisania dla niezainicjowanej struktury, która nie jest pusta. Bez zmian od 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)
Alokacja tablicy
Alokacja tablicy ignoruje dowolny konstruktor bez parametrów i generuje zero elementów. Brak zmian względem C#9.
// struct S { public S() { } }
var a = new S[1]; // constructor ignored, no warning
Wartość domyślna parametru new()
Wartość domyślna parametru new()
wiąże się z konstruktorem bez parametrów, jeśli jest publiczny (i zgłasza błąd, że wartość nie jest stała); w przeciwnym razie wystąpienie ma wartość zero.
Bez zmian od 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
Ograniczenia parametrów typu: new()
i struct
Ograniczenia parametrów typu new()
i struct
wymagają, aby konstruktor bez parametrów, jeśli jest zdefiniowany, był public
(zobacz Satysfakcjonujące ograniczenia — §8.4.5).
Kompilator zakłada, że wszystkie struktury spełniają new()
i struct
ograniczenia.
Bez zmian od 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()
jest emitowany jako wywołanie System.Activator.CreateInstance<T>()
, a kompilator zakłada, że implementacja CreateInstance<T>()
wywołuje konstruktor bez parametrów public
, jeśli jest zdefiniowany.
za pomocą programu .NET Framework Activator.CreateInstance<T>()
wywołuje konstruktor bez parametrów, jeśli ograniczenie jest where T : new()
, ale wydaje się ignorować konstruktor bez parametrów, jeśli ograniczenie jest where T : struct
.
Parametry opcjonalne
Konstruktory z parametrami opcjonalnymi nie są uważane za konstruktory bez parametrów. Brak zmian względem 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
Metadane
Jawne konstruktory wystąpień bez parametrów będą emitowane do metadanych.
Konstruktory wystąpienia struktury bez parametrów publicznych zostaną zaimportowane z metadanych; Konstruktory wystąpień innych niż publiczne zostaną zignorowane. Bez zmian względem C#9.
Zobacz też
Spotkania projektowe
- 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