Megosztás a következőn keresztül:


Paraméter nélküli szerkezetkonstruktorok

Jegyzet

Ez a cikk egy funkcióspecifikáció. A specifikáció a funkció tervezési dokumentumaként szolgál. Tartalmazza a specifikáció javasolt módosításait, valamint a funkció tervezése és fejlesztése során szükséges információkat. Ezeket a cikkeket mindaddig közzéteszik, amíg a javasolt specifikációmódosításokat nem véglegesítik, és be nem építik a jelenlegi ECMA-specifikációba.

A szolgáltatás specifikációja és a befejezett implementáció között eltérések lehetnek. Ezeket a különbségeket a vonatkozó nyelvi tervezési értekezlet (LDM) megjegyzései rögzítik.

A funkcióspektusok C# nyelvi szabványba való bevezetésének folyamatáról a specifikációkcímű cikkben olvashat bővebben.

Bajnoki probléma: https://github.com/dotnet/csharplang/issues/99

Összefoglalás

A szerkezettípusok paraméter nélküli konstruktorainak és példánymező-inicializálóinak támogatása.

Motiváció

Az explicit paraméter nélküli konstruktorok jobban szabályoznák a szerkezettípus minimálisan létrehozott példányait. A példánymező-inicializálók több konstruktor egyszerűsített inicializálását teszik lehetővé. Ezek együttesen betömnének egy nyilvánvaló rést a struct és class deklarációk között.

A mező inicializálóinak támogatása lehetővé tenné a mezők inicializálását record struct deklarációkban az elsődleges konstruktor explicit implementálása nélkül.

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

Ha a szerkezetmező-inicializálók támogatottak a paraméterekkel rendelkező konstruktorok esetében, természetesnek tűnik, hogy ezt a paraméter nélküli konstruktorokra is kiterjesztik.

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

Javaslat

Példánymező-inicializálók

Egy szerkezet példánymező-deklarációi inicializálókat is tartalmazhatnak.

Az osztálymező-inicializálókhoz hasonlóan §15.5.6.3:

Egy példánymező változó inicializálója nem hivatkozhat a létrehozott példányra.

Hiba jelenik meg, ha egy szerkezet mező-inicializálókkal rendelkezik, és nincs deklarált példánykonstruktor, mivel a mező inicializálói nem lesznek futtatva.

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

Konstruktorok

A szerkezetek paraméter nélküli példánykonstruktort deklarálhatnak.

A paraméter nélküli példánykonstruktor minden szerkezettípusra érvényes, beleértve a struct, readonly struct, ref structés record struct.

Ha nincs paraméter nélküli példánykonstruktor deklarálva, a szerkezet (lásd §16.4.9) ...

implicit módon van egy paraméter nélküli példánykonstruktor, amely mindig azt az értéket adja vissza, amely abból ered, hogy az összes értéktípusmezőt az alapértelmezett értékre állítja, az összes referenciatípus-mezőt pedig null értékre.

Módosítók

A paraméter nélküli példányszerkezet-konstruktort publickell deklarálni.

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

A nem nyilvános konstruktorok figyelmen kívül lesznek hagyva a metaadatokból származó típusok importálásakor.

A konstruktorok deklarálhatók extern vagy unsafe. A konstruktorok nem partial.

Mező inicializálóinak végrehajtása

Példányváltozó-inicializálók (§15.11.3) az alábbiak szerint módosul:

Ha egy osztály példánykonstruktor nem rendelkezik konstruktor-inicializálóval, vagy az base(...)konstruktor inicializálójával rendelkezik, a konstruktor implicit módon végrehajtja az osztályban deklarált példánymezők variable_initializers által meghatározott inicializálásokat. Ez a hozzárendelések sorozatának felel meg, amelyeket közvetlenül a konstruktorba való belépéskor és a közvetlen alaposztály-konstruktor implicit meghívása előtt hajtanak végre.

Ha egy szerkezetpéldány-konstruktor nem rendelkezik konstruktor-inicializálóval, a konstruktor implicit módon végrehajtja a szerkezetében deklarált példánymezők variable_initializers által megadott inicializálásokat. Ez olyan hozzárendelések sorozatának felel meg, amelyeket a konstruktorba való belépéskor azonnal végrehajtanak.

Ha egy szerkezet konstruktora rendelkezik egy this() konstruktor inicializálóval, amely a alapértelmezett paraméter nélküli konstruktor-t képviseli, a deklarált konstruktor automatikusan törli az összes példánymezőt, és végrehajtja a szerkezetében deklarált példánymezők variable_initializeráltal meghatározott inicializálásait. Azonnal a konstruktorba lépéskor az összes értéktípusú mező az alapértelmezett értékre van állítva, és az összes hivatkozástípusú mező nullértékre van állítva. Közvetlenül ezt követően a variable_initializers-nek megfelelő hozzárendelések sorozata lesz végrehajtva.

Határozott hozzárendelés

A példánymezőket (a fixed mezők kivételével) mindenképpen hozzá kell rendelni az olyan szerkezetpéldány-konstruktorokhoz, amelyek nem rendelkeznek this() inicializálóval (lásd §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; }
}

Nincs base() inicializáló

A base() inicializáló nem engedélyezett a szerkezetkonstruktorokban.

A fordító nem ad ki hívást a base System.ValueType konstruktorra a struktúra példánykonstruktoraiból.

record struct

Hiba jelenik meg, ha egy record struct mező inicializálókkal rendelkezik, és nem tartalmaz elsődleges konstruktort és példánykonstruktorokat, mivel a mező inicializálói nem lesznek futtatva.

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

Az üres paraméterlistával rendelkező record struct paraméter nélküli elsődleges konstruktort kap.

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

A record struct explicit paraméter nélküli konstruktorának rendelkeznie kell egy this inicializálóval, amely meghívja az elsődleges konstruktort vagy egy explicit módon deklarált konstruktort.

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

Mezők

Az implicit módon definiált paraméter nélküli konstruktor a mezőtípusok paraméter nélküli konstruktorainak meghívása helyett nullára állítja a mezőket. A rendszer nem jelez figyelmeztetést a mezőkonstruktorok figyelmen kívül hagyása miatt. Nincs változás a C#9-ről.

struct S0
{
    public S0() { }
}

struct S1
{
    S0 F; // S0 constructor ignored
}

struct S<T> where T : struct
{
    T F; // constructor (if any) ignored
}

default kifejezés

default figyelmen kívül hagyja a paraméter nélküli konstruktort, és nullázott példányt hoz létre. Nincs változás a C#9-ről.

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

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

new()

Az objektum létrehozása meghívja a paraméter nélküli konstruktort, ha nyilvános; ellenkező esetben a példány nullázva van. Nincs változás a C#9-ről.

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

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

A figyelmeztető hullám figyelmeztetést jelenthet a new() olyan szerkezettípussal való használatára vonatkozóan, amely konstruktorokkal rendelkezik, de nincs paraméter nélküli konstruktor. A rendszer nem jelent figyelmeztetést, ha egy ilyen struktúratípust helyettesít egy new() vagy struct korlátozással rendelkező típusparaméterhez.

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

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

Nem inicializált értékek

A nem explicit módon inicializált struktúratípus helyi vagy mezői nullázva lesznek. A fordító határozott hozzárendelési hibát jelez egy nem üres, inicializálatlan struktúra esetében. Nincs változás a C#9-ről.

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)

Tömbfoglalás

A tömbfoglalás figyelmen kívül hagy minden paraméter nélküli konstruktort, és nullázott elemeket hoz létre. Nincs változás a C#9-ről.

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

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

Paraméter alapértelmezett értéke new()

A new() paraméter alapértelmezett értéke akkor kapcsolódik a paraméter nélküli konstruktorhoz, ha nyilvános (és hibát jelez, hogy az érték nem állandó); ellenkező esetben a példány nullázva van. Nincs változás a C#9-ről.

// 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

Típusparaméter-korlátozások: new() és struct

A new() és struct típusparaméter-megkötések megkövetelik, hogy a paraméter nélküli konstruktor public legyen definiálva (lásd: Megfelelési kényszerek – §8.4.5).

A fordító feltételezi, hogy minden szerkezet megfelel new() és struct korlátozásoknak. Nincs változás a C#9-ről.

// 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() hívásként kerül kibocsátásra a System.Activator.CreateInstance<T>()-hez, és a fordító feltételezi, hogy a CreateInstance<T>() megvalósítása meghívja a public paraméter nélküli konstruktort, ha meg van határozva.

.NET-keretrendszer esetén Activator.CreateInstance<T>() meghívja a paraméter nélküli konstruktort, ha a kényszer where T : new(), de úgy tűnik, hogy figyelmen kívül hagyja a paraméter nélküli konstruktort, ha a kényszer where T : struct.

Választható paraméterek

Az opcionális paraméterekkel rendelkező konstruktorok nem tekinthetők paraméter nélküli konstruktoroknak. Nincs változás a C#9-ről.

struct S1 { public S1(string s = "") { } }
struct S2 { public S2(params object[] args) { } }

_ = new S1(); // ok: ignores constructor
_ = new S2(); // ok: ignores constructor

Metaadatok

A rendszer explicit paraméter nélküli szerkezetpéldány-konstruktorokat bocsát ki a metaadatoknak.

A nyilvános paraméter nélküli szerkezetpéldány-konstruktorok metaadatokból lesznek importálva; a nem nyilvános szerkezetpéldány-konstruktorok figyelmen kívül lesznek hagyva. Nincs változás a C#9-ről.

Lásd még:

Tervezési értekezletek