Parameterlösa strukturkonstruktorer
Notera
Den här artikeln är en funktionsspecifikation. Specifikationen fungerar som designdokument för funktionen. Den innehåller föreslagna specifikationsändringar, tillsammans med information som behövs under utformningen och utvecklingen av funktionen. Dessa artiklar publiceras tills de föreslagna specifikationsändringarna har slutförts och införlivats i den aktuella ECMA-specifikationen.
Det kan finnas vissa skillnader mellan funktionsspecifikationen och den slutförda implementeringen. Dessa skillnader samlas in i de relevanta anteckningarna från LDM (möte om språkdesign).
Du kan läsa mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.
Champion-problem: https://github.com/dotnet/csharplang/issues/99
Sammanfattning
Stöd för parameterlösa konstruktorer och instansfältsinitierare för strukturegenskaper.
Motivation
Explicita parameterlösa konstruktorer skulle ge mer kontroll över minimalt konstruerade instanser av struct-typen.
Instansfältinitierare skulle tillåta förenklad initiering mellan flera konstruktorer.
Tillsammans skulle dessa överbrygga en uppenbar klyfta mellan struct
och class
förklaringar.
Stöd för fältinitierare skulle också tillåta initiering av fält i record struct
-deklarationer utan att uttryckligen implementera den primära konstruktorn.
record struct Person(string Name)
{
public object Id { get; init; } = GetNextId();
}
Om struct-fältinitierare stöds för konstruktorer med parametrar verkar det naturligt att utöka det även till parameterlösa konstruktorer.
record struct Person()
{
public string Name { get; init; }
public object Id { get; init; } = GetNextId();
}
Förslag
Instansfältinitierare
Instansfältdeklarationer för en struct kan innehålla initialiserare.
Precis som med klassfältinitierare §15.5.6.3:
En variabelinitierare för ett instansfält kan inte referera till den instans som skapas.
Ett fel rapporteras om en struct har fältinitierare och inga deklarerade instanskonstruktorer eftersom fältinitierarna inte körs.
struct S { int F = 42; } // error: 'struct' with field initializers must include an explicitly declared constructor
Konstruktörer
En struct kan deklarera en parameterlös instanskonstruktor.
En parameterlös instanskonstruktor är giltig för alla structtyper, inklusive struct
, readonly struct
, ref struct
och record struct
.
Om ingen parameterlös instanskonstruktor deklareras, strukturen (se §16.4.9) ...
implicit har en parameterlös instanskonstruktor som alltid returnerar värdet som är resultatet av att ange alla värdetypsfält till deras standardvärde och alla referenstypfält till null.
Modifierare
En struct-konstruktor för parameterlös instans måste deklareras public
.
struct S0 { } // ok
struct S1 { public S1() { } } // ok
struct S2 { internal S2() { } } // error: parameterless constructor must be 'public'
Icke-offentliga konstruktorer ignoreras vid import av typer från metadata.
Konstruktorer kan deklareras som extern
eller unsafe
.
Konstruktorer kan inte vara partial
.
Exekvera fältinitierare
Instansvariabelinitierare (§15.11.3) ändras enligt följande:
När en klass instanskonstruktör inte har någon konstruktorinitierare, eller om den har en konstruktorinitierare i form
base(...)
, utför konstruktorn implicit de initialiseringar som anges av variable_initializerför instansfälten som deklarerats i klassen. Detta motsvarar en sekvens med tilldelningar som körs omedelbart när man går in i konstruktorn och före det implicita anropet av den direkta basklasskonstruktorn.När en struct-instans-konstruktor inte har någon konstruktor-initierare utför konstruktorn implicit de initieringar som anges av variable_initializerför instansfälten som deklarerats i dess struct. Detta motsvarar en sekvens med tilldelningar som körs omedelbart vid inmatning till konstruktorn.
När en struct-instanskonstruktor har en
this()
konstruktorinitierare som representerar den standardparameterlösa konstruktornrensar den deklarerade konstruktorn implicit alla instansfält och utför de initieringar som anges av variable_initializerför de instansfält som deklarerats i dess struct. Omedelbart efter inmatningen till konstruktorn anges alla värdetypsfält till standardvärdet och alla fält av referenstyp är inställda pånull
. Omedelbart därefter körs en sekvens med tilldelningar som motsvarar variable_initializers.
Bestämd tilldelning
Instansfält (förutom fixed
-fält) måste vara tydligt tilldelade i struct instanskonstruktorer som inte har en this()
-initialiserare (se §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; }
}
Ingen base()
initialiserare
En base()
initialiserare tillåts inte i strukturskonstruktörer.
Kompilatorn skickar inte ett anrop till baskonstruktorn System.ValueType
från struct-instanskonstruktorer.
record struct
Ett fel rapporteras om en record struct
har fältinitierare och inte innehåller någon primär konstruktor eller några instanskonstruktorer eftersom fältinitierarna inte körs.
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
En record struct
med en tom parameterlista har en primär konstruktor utan parameter.
record struct R3(); // primary .ctor: public R3() { }
record struct R4() { int F = 42; } // primary .ctor: public R4() { F = 42; }
En explicit parameterlös konstruktor i en record struct
måste ha en this
initierare som anropar den primära konstruktorn eller en uttryckligen deklarerad konstruktor.
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;
}
Fält
Den implicit definierade parameterlösa konstruktorn nollar fält i stället för att anropa några parameterlösa konstruktorer för fälttyperna. Inga varningar rapporteras om att fältkonstruktorer ignoreras. Ingen ändring från 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
uttryck
default
ignorerar den parameterlösa konstruktorn och genererar en nollad instans.
Ingen ändring från C#9.
// struct S { public S() { } }
_ = default(S); // constructor ignored, no warning
new()
Skapande av objekt anropar den parameterlösa konstruktorn om den är offentlig. annars nollställs instansen. Ingen ändring från C#9.
// public struct PublicConstructor { public PublicConstructor() { } }
// public struct PrivateConstructor { private PrivateConstructor() { } }
_ = new PublicConstructor(); // call PublicConstructor::.ctor()
_ = new PrivateConstructor(); // initobj PrivateConstructor
En varningsvåg kan rapportera en varning för användning av new()
med en structtyp som har konstruktorer men ingen parameterlös konstruktor.
Ingen varning rapporteras när en sådan structtyp ersätts med en typparameter med en new()
eller struct
begränsning.
struct S { public S(int i) { } }
static T CreateNew<T>() where T : new() => new T();
_ = new S(); // no warning called
_ = CreateNew<S>(); // ok
Uninitialiserade värden
Ett lokalt eller ett fält av en strukturtyp som inte uttryckligen initieras nollställs. Kompilatorn rapporterar ett definitivt tilldelningsfel för en ennitialiserad struct som inte är tom. Ingen ändring från 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)
Matrisallokering
Matrisallokering ignorerar alla parameterlösa konstruktorer och genererar nollade element. Ingen ändring från C#9.
// struct S { public S() { } }
var a = new S[1]; // constructor ignored, no warning
Parameterstandardvärde new()
Ett parameterstandardvärde för new()
binder till den parameterlösa konstruktorn om det är offentligt (och rapporterar ett fel om att värdet inte är konstant); annars nollställs instansen.
Ingen ändring från 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
Ange parameterbegränsningar: new()
och struct
Parameterbegränsningarna new()
och struct
kräver att den parameterlösa konstruktorn är public
om den är definierad (se tillfredsställande begränsningar – §8.4.5).
Kompilatorn förutsätter att alla structs uppfyller new()
och struct
begränsningar.
Ingen ändring från 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()
skickas som ett anrop till System.Activator.CreateInstance<T>()
, och kompilatorn förutsätter att implementeringen av CreateInstance<T>()
anropar public
parameterlös konstruktor om den definieras.
Med .NET Framework anropar Activator.CreateInstance<T>()
den parameterlösa konstruktorn om villkoret är where T : new()
men verkar ignorera den parameterlösa konstruktorn om villkoret är where T : struct
.
Valfria parametrar
Konstruktorer med valfria parametrar betraktas inte som parameterlösa konstruktorer. Ingen ändring från 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
Metadata
Explicita parameterlösa strukturinstanskonstruktorer kommer att genereras till metadata.
Konstruktorer för offentliga parameterlösa struct-instanser importeras från metadata. icke-offentliga struct-instanskonstruktorer ignoreras. Ingen ändring från C#9.
Se även
Designa möten
- 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