Delen via


Parameterloze struct constructoren

Notitie

Dit artikel is een functiespecificatie. De specificatie fungeert als het ontwerpdocument voor de functie. Het bevat voorgestelde specificatiewijzigingen, samen met informatie die nodig is tijdens het ontwerp en de ontwikkeling van de functie. Deze artikelen worden gepubliceerd totdat de voorgestelde specificaties zijn voltooid en opgenomen in de huidige ECMA-specificatie.

Er kunnen enkele verschillen zijn tussen de functiespecificatie en de voltooide implementatie. De verschillen worden vastgelegd in de relevante notulen van het Language Design Meeting (LDM) .

Meer informatie over het proces voor het aannemen van functiespeclets in de C#-taalstandaard vindt u in het artikel over de specificaties.

Samenvatting

Ondersteuning voor parameterloze constructors en initialisatie van instantievelden voor structtypen.

Motivatie

Expliciete parameterloze constructors geven meer controle over minimaal samengestelde exemplaren van het structtype. Initialisatoren van instantievelden zouden een vereenvoudigde initialisatie van meerdere constructors mogelijk maken. Samen zouden deze een duidelijke kloof tussen struct en class declaraties dichten.

Ondersteuning voor veldinitialisaties zou ook initialisatie van velden in record struct declaraties toestaan zonder expliciet de primaire constructor te implementeren.

record struct Person(string Name)
{
    public object Id { get; init; } = GetNextId();
}

Als structveld initialisaties worden ondersteund voor constructors met parameters, lijkt het natuurlijk om dat ook uit te breiden naar parameterloze constructors.

record struct Person()
{
    public string Name { get; init; }
    public object Id { get; init; } = GetNextId();
}

Voorstel

Initialisators voor exemplaarvelden

Instantievelddeclaraties voor een struct kunnen initialisaties bevatten.

Net als bij initialisatie van klassenvelden §15.5.6.3:

Een variabele initialisatiefunctie voor een exemplaarveld kan niet verwijzen naar het exemplaar dat wordt gemaakt.

Er wordt een fout gerapporteerd als een struct veldinitialisaties heeft en geen gedeclareerde exemplaarconstructors, omdat de veldinitialisaties niet worden uitgevoerd.

struct S { int F = 42; } // error: 'struct' with field initializers must include an explicitly declared constructor

Constructeurs

Een struct kan een constructor voor een parameterloze instantie declareren.

Een constructor voor parametersloze exemplaren is geldig voor alle structtypen, waaronder struct, readonly struct, ref structen record struct.

Als er geen constructor voor een parameterloze instantie wordt gedeclareerd, wordt de struct (zie §16.4.9) ...

impliciet heeft een instantieconstructor zonder parameter die altijd de waarde retourneert die het resultaat is van het instellen van alle waardetypevelden op de standaardwaarde en alle verwijzingstypevelden op null.

Aanpassingen

Een parameterloze instantieconstructor moet worden gedeclareerd public.

struct S0 { }                   // ok
struct S1 { public S1() { } }   // ok
struct S2 { internal S2() { } } // error: parameterless constructor must be 'public'

Niet-openbare constructors worden genegeerd bij het importeren van typen uit metagegevens.

Constructors kunnen gedeclareerd worden als extern of unsafe. Constructors kunnen niet partialzijn.

Veldinitialisaties uitvoeren

Instantiëringsvariabelen-initialiseerders (§15.11.3) worden als volgt gewijzigd:

Wanneer een klasse instantieconstructor geen constructor-initialisator heeft, of een constructor-initialisator van de vorm base(...)heeft, voert die constructor impliciet de initialisaties uit die zijn gespecificeerd door de variabele-initialisatiesvan de instantievelden die in de klasse zijn gedeclareerd. Dit komt overeen met een reeks toewijzingen die direct worden uitgevoerd na invoer aan de constructor en vóór de impliciete aanroep van de directe basisklasseconstructor.

Wanneer een constructor van een struct-exemplaar geen constructor-initialisatie heeft, voert die constructor impliciet de initialisaties uit die zijn opgegeven door de variable_initializers van de instantievelden die in de struct zijn gedeclareerd. Dit komt overeen met een reeks toewijzingen die direct worden uitgevoerd bij het betreden van de constructor.

Wanneer een constructor van een struct-exemplaar een this() constructor-initialisatiefunctie heeft die de standaardconstructor zonder parameterlozevertegenwoordigt, wist de gedeclareerde constructor impliciet alle exemplaarvelden en voert de initialisaties uit die zijn opgegeven door de variable_initializers van de instantievelden die in de struct zijn gedeclareerd. Onmiddellijk na invoer in de constructor worden alle waardetypevelden ingesteld op de standaardwaarde en worden alle verwijzingstypevelden ingesteld op null. Onmiddellijk daarna wordt een reeks toewijzingen uitgevoerd die overeenkomen met de variable_initializers.

Definitieve opdracht

Instantievelden (behalve fixed velden) moeten zeker worden toegewezen in struct-exemplaarconstructors die geen this() initialisatiefunctie hebben (zie §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; }
}

Geen base() initialisatiefunctie

Een base() initialisatiefunctie is niet toegestaan in struct constructors.

De compiler verzendt geen aanroep naar de basis-System.ValueType constructor van struct-exemplaarconstructors.

record struct

Er wordt een fout gerapporteerd als een record struct veld initialisatieprogramma's heeft en geen primaire constructor of instantieconstructors bevat, omdat de veld initialisaties niet worden uitgevoerd.

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

Een record struct met een lege parameterlijst heeft een primaire constructor zonder parameters.

record struct R3();                // primary .ctor: public R3() { }
record struct R4() { int F = 42; } // primary .ctor: public R4() { F = 42; }

Een expliciete parameterloze constructor in een record struct moet een this initialisatiefunctie hebben die de primaire constructor of een expliciet gedeclareerde constructor aanroept.

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;
}

Velden

De impliciet gedefinieerde parameterloze constructor zal velden nul maken in plaats van parameterloze constructors voor de veldtypen aan te roepen. Er worden geen waarschuwingen gemeld dat veldconstructors worden genegeerd. Geen wijziging van 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-expressie

default negeert de parameterloze constructor en genereert een nulexemplaar. Geen wijziging van C#9.

// struct S { public S() { } }

_ = default(S); // constructor ignored, no warning

new()

Het maken van objecten roept de parameterloze constructor aan indien openbaar; anders is het exemplaar nul. Geen wijziging van C#9.

// public struct PublicConstructor { public PublicConstructor() { } }
// public struct PrivateConstructor { private PrivateConstructor() { } }

_ = new PublicConstructor();  // call PublicConstructor::.ctor()
_ = new PrivateConstructor(); // initobj PrivateConstructor

Een waarschuwingsgolf kan een waarschuwing geven voor het gebruik van new() met een structtype dat constructoren heeft, maar geen parameterloze constructor. Er wordt geen waarschuwing gerapporteerd bij het substitueren van een structtype voor een typeparameter met een new() of struct restrictie.

struct S { public S(int i) { } }
static T CreateNew<T>() where T : new() => new T();

_ = new S();        // no warning called
_ = CreateNew<S>(); // ok

Niet-geïnitialiseerde waarden

Een lokaal of veld van een structtype dat niet expliciet is geïnitialiseerd, wordt op nul gezet. De compiler rapporteert een definitieve toewijzingsfout voor een niet-geïnitialiseerde struct die niet leeg is. Geen wijziging van 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)

Arraytoewijzing

Bij arraytoewijzing worden een constructor zonder parameters genegeerd en worden elementen met de waarde nul gegenereerd. Geen wijziging van C#9.

// struct S { public S() { } }

var a = new S[1]; // constructor ignored, no warning

Standaardwaarde van parameter new()

Een parameterstandaardwaarde van new() bindt aan de parameterloze constructor als openbaar (en rapporteert een fout dat de waarde niet constant is); anders is het exemplaar nul. Geen wijziging van 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

Typeparameterbeperkingen: new() en struct

Voor de parameterbeperkingen new() en struct moet de parameterloze constructor worden public indien gedefinieerd (zie Bevredigende beperkingen - §8.4.5).

De compiler gaat ervan uit dat alle structs voldoen aan new() en struct beperkingen. Geen wijziging van 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() wordt verzonden als een aanroep naar System.Activator.CreateInstance<T>()en de compiler gaat ervan uit dat de implementatie van CreateInstance<T>() de public parameterloze constructor aanroept als deze is gedefinieerd.

Met .NET Framework roept Activator.CreateInstance<T>() de parameterloze constructor aan als de beperking where T : new() is, maar de parameterloze constructor wordt genegeerd als de beperking is where T : struct.

Optionele parameters

Constructors met optionele parameters worden niet beschouwd als parameterloze constructors. Geen wijziging van 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

Metagegevens

Expliciete constructors van structuur-instanties zonder parameters worden opgenomen in de metadata.

Constructors voor openbare parameterloze struct-exemplaren worden geïmporteerd uit metagegevens; niet-openbare constructors voor struct-exemplaren worden genegeerd. Geen wijziging van C#9.

Zie ook

Ontwerpvergaderingen