Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
16.1 Általános
A szerkezetek hasonlóak az osztályokhoz, amelyek adatstruktúrákat jelölnek, amelyek adattagokat és függvénytagokat tartalmazhatnak. Az osztályoktól eltérően azonban a szerkezetek értéktípusok, és nem igényelnek halomfoglalást. Egy típus változója struct közvetlenül tartalmazza a típus adatait struct, míg egy osztálytípus változója az adatokra mutató hivatkozást, az utóbbit objektumnak nevezik.
Megjegyzés: A szerkezetek különösen hasznosak az értékszemantikával rendelkező kis adatstruktúrák esetében. Az összetett számok, a koordinátarendszer pontjai vagy a szótár kulcs-érték párjai mind jó példák a szerkezetekre. Ezeknek az adatstruktúráknak az a legfontosabb, hogy kevés adattagjuk van, és nem igényelnek öröklési vagy hivatkozási szemantikát, hanem kényelmesen implementálhatók értékszemantikával, ahol a hozzárendelés a hivatkozás helyett az értéket másolja. végjegyzet
A 8.3.5. §-ban leírtak szerint a C# által biztosított egyszerű típusok, például inta , doubleés bool, valójában az összes strukturált típus.
16.2 Szerkezetmegjelölő nyilatkozatok
16.2.1 Általános
A struct_declaration egy type_declaration (14.8. §), amely új szerkezetet deklarál:
struct_declaration
: non_record_struct_declaration
| record_struct_declaration
;
non_record_struct_declaration
: attributes? struct_modifier* 'ref'? 'partial'? 'struct'
identifier type_parameter_list? struct_interfaces?
type_parameter_constraints_clause* struct_body ';'?
;
record_struct_declaration
: attributes? struct_modifier* 'partial'? 'record' 'struct'
identifier type_parameter_list? delimited_parameter_list? struct_interfaces?
type_parameter_constraints_clause* record_struct_body
;
record_struct_body
: struct_body ';'?
| ';'
;
A struct_declaration nem rekord típusú vagy rekordstruktúra.
A non_record_struct_declarationegy választható attribútumkészletből (23. §), majd egy választható struct_modifier-készletből(16.2.2. §), majd egy választható ref módosítóból (16.2.3. §), majd egy választható részleges módosítóból (15.2.7. §) áll, amelyet a kulcsszó struct és a szerkezetnek nevező azonosító követ, amelyet egy opcionális type_parameter_list specifikáció követ (15.2.3. §), egy választható struct_interfaces specifikáció (16.2.5.§), majd egy opcionális type_parameter_constraints-záradék specifikáció (15.2.5. §), majd egy struct_body (16.2.6. §), amelyet opcionálisan pontosvessző követ.
A record_struct_declarationegy választható attribútumkészletből (23. §), majd egy választható struct_modifier-készletből(16.2.2. §), majd egy választható részleges módosítóból (15.2.7.§) áll, amelyet a kulcsszó record, majd struct a kulcsszó és a szerkezetet nevesítő azonosító követ, majd egy választható type_parameter_list specifikáció (15.2.3. §), majd egy választható delimited_parameter_list specifikáció (§15.2.1), majd egy opcionális struct_interfaces specifikáció (§16.2.5), majd egy opcionális type_parameter_constraints záradék specifikáció (§15.2.5), majd egy record_struct_body.
A struct_declaration csak akkor szállíthat type_parameter_constraints_clausetype_parameter_list.
A type_parameter_list ellátó struct_declaration általános strukturált deklaráció. Ezenkívül az általános osztálydeklarációkba vagy általános szerkezetdeklarációkba ágyazott szerkezetek maguk is általános szerkezet deklarációk, mivel a szerkezetezett típus létrehozásához meg kell adni a típus argumentumait (8.4. §).
A módosító non_record_struct_declaration nem lehet struct_interfaces része.ref
A delimited_parameter_list rendelkező record_struct_declarationpozíciórekord-szerkezetet deklarál.
Legfeljebb egy record_struct_declaration tartalmazhat partialdelimited_parameter_list.
A delimited_parameter_list paramétereinek azonban nem lehetnek refmódosítói out vagy this módosítói, in és params a módosítók engedélyezettek.
Egy record_struct_declaration esetében a record_struct_bodys {}, {};és ; egyenértékűek. Mind azt jelzik, hogy csak a fordító által szintetizált tagok (16.4. §).
16.2.2 Szerkezetmódosítók
A struct_declaration opcionálisan struct_modifiersorozatot is tartalmazhatnak:
struct_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'readonly'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (24.2. §) csak nem biztonságos kódban érhető el (24. §).
Fordítási idő hibája, hogy ugyanaz a módosító többször is megjelenik egy szerkezetdeklarációban.
readonlyA szerkezetmegjelölő deklaráció módosítóinak jelentése nem azonos az osztálydeklarációéval (15.2.2. §).
A readonly módosító azt jelzi, hogy a struct_declaration olyan típust deklarál, amelynek példányai nem módosíthatók.
Az olvasható szerkezetek a következő korlátozásokkal rendelkeznek:
- Az egyes példánymezőket is deklarálni
readonlykell . - Nem deklarál semmilyen mezőszerű eseményt (15.8.2. §).
Ha egy egyszerű szerkezet egy példányát egy metódusnak ad át, a this rendszer bemeneti argumentumként/paraméterként kezeli, amely nem engedélyezi az írási hozzáférést a példánymezőkhöz (a konstruktorok kivételével).
16.2.3 Ref módosító
A ref módosító azt jelzi, hogy a non_record_struct_declaration deklarál egy típust, amelynek példányai a végrehajtási veremhez vannak lefoglalva. Ezeket a típusokat ref struct típusoknak nevezzük. A ref módosító kijelenti, hogy a példányok ref-szerű mezőket tartalmazhatnak, és nem másolhatók ki a biztonságos környezetből (16.5.15. §). A refstruktúra biztonságos környezetének meghatározására vonatkozó szabályokat a 16.5.15.
Fordítási idejű hiba, ha az alábbi környezetekben refstruktúratípust használ:
- Tömb elemtípusaként.
- Osztály vagy szerkezet deklarált típusaként, amely nem rendelkezik módosítóval
ref. - Típusargumentumként.
- A rekordelem típusaként.
- Aszinkron metódusban.
- Egy iterátorban.
- A metóduscsoport példánymetódusból delegált típusra történő konvertálásának fogadótípusaként.
- Rögzített változóként egy lambdakifejezésben vagy egy helyi függvényben.
Emellett a következő korlátozások vonatkoznak egy ref struct típusra:
- A
ref structtípust nem szabad bekeretelni vagySystem.ValueTypebeszúrniSystem.Object. - Egy
ref structtípus nem deklarálható interfész implementálásához. - A típusban
objectdeklarált vagy abbanSystem.ValueTypedeklarált, de felül nem bíráltref structpéldánymetódus nem hívható meg ilyenref structtípusú fogadóval.
Megjegyzés: Az A
ref structnem deklarálhatasyncpéldánymetódusokat, és nem használhat példánymetóduson belüli utasítástyield returnyield break, mert az implicitthisparaméter nem használható ezekben a kontextusokban. végjegyzet
Ezek a korlátozások biztosítják, hogy egy változótípus ref struct ne hivatkozzon a már nem érvényes veremmemóriára vagy a már nem érvényes változókra.
16.2.4 Részleges módosító
A partial módosító azt jelzi, hogy ez a struct_declaration részleges típusdeklaráció.
A 15.2.7.
16.2.5 Strukturáló felületek
A szerkezet deklarációja tartalmazhat egy struct_interfaces specifikációt, amely esetben a szerkezet állítólag közvetlenül implementálja az adott interfésztípusokat. Egy strukturált szerkezettípus esetében, beleértve az általános típusdeklarációban deklarált beágyazott típust is (15.3.9.7. §) minden implementált illesztőtípust az adott interfész minden egyes type_parameter helyettesítve a létrehozott típus megfelelő type_argument .
struct_interfaces
: ':' interface_type_list
;
A részleges szerkezet deklaráció több részén (15.2.7. §) található interfészek kezelését a 15.2.4.3.
A felület implementációit a 19.6.
16.2.6 Szerkezettörzs
A szerkezet struct_body határozza meg a szerkezet tagjait.
struct_body
: '{' struct_member_declaration* '}'
;
16.3 Tagok strukturálás
16.3.1 Általános
A szerkezet tagjai a struct_member_declarationáltal bevezetett tagokból és a típusból System.ValueTypeöröklődő tagokból állnak. Rekordstruktúra esetén a tagkészlet tartalmazza a fordító által létrehozott szintetizált tagokat is (§synth-members).
struct_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| operator_declaration
| constructor_declaration
| static_constructor_declaration
| type_declaration
| fixed_size_buffer_declaration // unsafe code support
;
fixed_size_buffer_declaration (24.8.2. §) csak nem biztonságos kódban érhető el (24. §).
Megjegyzés: A finalizer_declaration kivételével a class_member_declaration összes típusa is struct_member_declarations. végjegyzet
A 16.5. §-ban feljegyzett különbségek kivételével a 15.3–15.12. §-ban szereplő osztálytagok leírása a tagokat is alkalmazni kell.
Hiba, ha egy rekordstruktúra példánymezője nem biztonságos típusú.
16.3.2 Olvasható tagok
Egy példánytulajdonság, indexelő vagy esemény egy példánytag-definíciója vagy tartozéka, amely tartalmazza a readonly módosítót, a következő korlátozásokkal rendelkezik:
- A
thisparaméter egyref readonlyhivatkozás. - A tag nem rendelheti át újra a fogadó értékét
thisvagy egy példánymezőt. - A tag nem rendeli újra a fogadó példányhoz hasonló esemény értékét (15.8.2. §).
- Ha egy olvasható tag nem olvasható tagot hív meg, a hivatkozott
thisstruktúrát át kell másolni, hogy írható hivatkozást használjon azthisargumentumhoz.
Megjegyzés: A példánymezők tartalmazzák az automatikusan implementált tulajdonságokhoz használt rejtett háttérmezőt (15.7.4. §). végjegyzet
Példa: Egy olvasható tag módosíthatja egy példánymező által hivatkozott objektum állapotát, annak ellenére, hogy az olvasható tag nem rendelheti hozzá újra az adott példánytagot. Az alábbi kód egy példánymező újbóli hozzárendelését és módosítását mutatja be:
public struct S { private List<string> messages; public S(IEnumerable<string> messages) => this.messages = new List<string>(messages); public void InitializeMessages() => messages = new List<string>(); public readonly void AddMessage(string message) { if (messages == null) { throw new InvalidOperationException("Messages collection is not initialized."); } messages.Add(message); } }A
readonlymetódusAddMessagemegváltoztathatja egy üzenetlista állapotát. AInitializeMessagestag törölheti és újra inicializálhatja az üzenetek listáját. Ebben az esetbenAddMessageareadonlymódosító érvényes. Ebben az esetbenInitializeMessagesa módosító hozzáadásareadonlyérvénytelen. záró példa
16.4 Szintetizált rekordstruktúra tagjai
16.4.1 Általános
Rekordstruktúra esetén a tagok szintetizálva lesznek, kivéve, ha a record_struct_body egy "egyező" aláírással rendelkező tagot deklarálnak, vagy egy akadálymentes, "egyező" aláírással rendelkező, nem virtuális tag öröklődik. Két tag akkor tekinthető egyezőnek, ha ugyanazzal az aláírással rendelkezik, vagy "elrejtésnek" minősül egy öröklési forgatókönyvben. (Lásd: Aláírások és túlterhelés §7.6.)
A szintetizált tagokat az alábbi alklámok ismertetik.
16.4.2 Egyenlőségi tagok
A szintetizált egyenlőség tagjai hasonlóak a rekordosztályhoz (15.16.2.§), kivéve a null értékű ellenőrzések vagy öröklés hiányát EqualityContract.
A rekordstruktúra R az alábbiak szerint valósítja System.IEquatable<R> meg és foglalja magában a szintetizált, erős típusú túlterhelést Equals(R other), amely nyilvános:
public readonly bool Equals(R other);
Ez a módszer explicit módon deklarálható. Hiba azonban, ha az explicit deklaráció nem felel meg a várt aláírásnak vagy akadálymentességnek.
Ha Equals(R other) felhasználó által definiált (azaz nem szintetizált), de GetHashCode nem, figyelmeztetést kell létrehozni.
A szintetizált Equals(R) függvény akkor és csak akkor ad vissza értékettrue, TN ha a rekord egyes példánymezőinél fieldNSystem.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN)a mezőtípus trueértéke az .
A rekordstruktúra a következő módon deklarált operátorokkal egyenértékű szintetizált == és != operátorokat tartalmazza:
public static bool operator==(R r1, R r2) => r1.Equals(r2);
public static bool operator!=(R r1, R r2) => !(r1 == r2);
Az Equals operátor által == meghívott metódus a Equals(R other) fent megadott módszer. Az != operátor delegálja az operátort == . Hiba, ha az operátorok explicit módon vannak deklarálva.
A rekordstruktúra a következőképpen deklarált metódussal egyenértékű szintetizált felülbírálást tartalmaz:
public override readonly bool Equals(object? obj);
Hiba, ha a felülbírálás explicit módon van deklarálva. A szintetizált felülbírálásnak a rekordstruktúra helyét R kell visszaadniother is R temp && Equals(temp).
A rekordstruktúra a következőképpen deklarált metódussal egyenértékű szintetizált felülbírálást tartalmaz:
public override readonly int GetHashCode();
Ez a módszer explicit módon deklarálható.
Figyelmeztetést kell jelenteni, ha az egyik Equals(R) és GetHashCode() kifejezetten deklarált, de a másik módszer nem.
A szintetizált felülbírálás GetHashCode()int eredménye az egyes példánymezők fieldNTN értékeinek System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN) és a típusának egyesítésének fieldNeredménye.
Példa: Vegye figyelembe a következő rekordstruktúra szerkezetét:
record struct R1(T1 P1, T2 P2);Ehhez a szintetizált egyenlőségi tagok a következőhöz hasonlóak:
struct R1 : IEquatable<R1> { public T1 P1 { get; set; } public T2 P2 { get; set; } public override bool Equals(object? obj) => obj is R1 temp && Equals(temp); public bool Equals(R1 other) { return EqualityComparer<T1>.Default.Equals(P1, other.P1) && EqualityComparer<T2>.Default.Equals(P2, other.P2); } public static bool operator==(R1 r1, R1 r2) => r1.Equals(r2); public static bool operator!=(R1 r1, R1 r2) => !(r1 == r2); public override int GetHashCode() { return HashCode.Combine( EqualityComparer<T1>.Default.GetHashCode(P1), EqualityComparer<T2>.Default.GetHashCode(P2));záró példa
16.4.3 Nyomtatási tagok
A rekordstruktúra a következővel egyenértékű szintetizált metódust tartalmaz:
private bool PrintMembers(System.Text.StringBuilder builder);
Ez a metódus a következő feladatokat hajtja végre:
- A rekordstruktúra nyomtatható tagjai (nem statikus nyilvános mező és olvasható tulajdonságtagok) mindegyikéhez hozzáfűzi a tag nevét, majd a "
=" szöveget, majd a tag értékét a következővel elválasztva: ", “, - Igaz értéket ad vissza, ha a rekordstruktúra nyomtatható tagokkal rendelkezik.
Az értéktípussal rendelkező tagok értékét sztringábrázolássá kell alakítani.
Ha a rekord nyomtatható tagjai nem tartalmaznak olvasható tulajdonságot nem tartozékokkalreadonlyget, akkor a szintetizált PrintMembers tulajdonság .readonly A rekord mezőinek readonlyPrintMembers nem kell a metódusnak lenniük readonly.
A PrintMembers metódus explicit módon deklarálható. Hiba azonban, ha az explicit deklaráció nem felel meg a várt aláírásnak vagy akadálymentességnek.
A rekordstruktúra a következővel egyenértékű szintetizált metódust tartalmaz:
public override string ToString();
Ha a rekordstruktúra módszere PrintMembers , readonlyakkor a szintetizált ToString() módszernek kell lennie readonly.
Ez a módszer explicit módon deklarálható. Hiba, ha a kifejezett deklaráció nem felel meg a várt aláírásnak vagy akadálymentességnek.
Ez a metódus a következő feladatokat hajtja végre:
- Létrehoz egy példányt
StringBuilder, - Hozzáfűzi a rekordstruktúra nevét a szerkesztőhöz, majd a "
{", - Meghívja a rekordstruktúra metódusát
PrintMembers, amely a szerkesztőt adja meg, majd "" követi, ha igaz értéket ad vissza, - Hozzáfűzők "
}", - A szerkesztő tartalmát adja vissza a következővel
builder.ToString(): .
Példa: Vegye figyelembe a következő rekordstruktúra szerkezetét:
record struct R1(T1 P1, T2 P2);Ebben a rekordstruktúra esetében a szintetizált nyomtatási tagok a következőhöz hasonlóak lehetnek:
struct R1 : IEquatable<R1> { public T1 P1 { get; set; } public T2 P2 { get; set; } private bool PrintMembers(StringBuilder builder) { builder.Append(nameof(P1)); builder.Append(" = "); builder.Append(this.P1); // or builder.Append(this.P1.ToString()); // if P1 has a value type builder.Append(", "); builder.Append(nameof(P2)); builder.Append(" = "); builder.Append(this.P2); // or builder.Append(this.P2.ToString()); // if P2 has a value type return true; } public override string ToString() { var builder = new StringBuilder(); builder.Append(nameof(R1)); builder.Append(" { "); if (PrintMembers(builder)) builder.Append(" "); builder.Append("}"); return builder.ToString(); } }záró példa
16.4.4 A pozíciórekordok strukturált tagjai
16.4.4.1 Általános
Az előző alklámokban leírt tagok biztosítása mellett a helymeghatározó szerkezetek (16.2.1. §) a többi taggal azonos feltételekkel szintetizálják a további tagokat az alábbi alklámokban leírtak szerint.
16.4.4.2 Elsődleges konstruktor
A rekordstruktúra nyilvános konstruktorsal rendelkezik, amelynek aláírása megfelel a típusdeklaráció értékparamétereinek. Ezt nevezik a típus elsődleges konstruktorának. Hiba, ha egy elsődleges konstruktor és egy konstruktor ugyanazzal az aláírással rendelkezik, amely már megtalálható a szerkezetben. Ha a típusdeklaráció nem tartalmaz delimited_parameter_list, a rendszer nem hoz létre elsődleges konstruktort.
record struct R1 { public R1() { } // OK } record struct R2() { public R2() { } // error: 'R2' already defines // a constructor with the same parameter types }
A rekordstruktúra példánymező-deklarációi tartalmazhatnak változó inicializálókat. Ha nincs elsődleges konstruktor, a példány inicializálói a paraméter nélküli konstruktor részeként futnak. Ellenkező esetben futásidőben az elsődleges konstruktor végrehajtja a rekordstruktúra törzsében megjelenő példány-inicializálókat.
Ha egy rekordstruktúra elsődleges konstruktorsal rendelkezik, minden felhasználó által definiált konstruktornak rendelkeznie kell egy explicit this konstruktor-inicializálóval, amely meghívja az elsődleges konstruktort vagy egy explicit módon deklarált konstruktort.
Az elsődleges konstruktor paraméterei és a rekordstruktúra tagjai hatóköre a példánymezők vagy tulajdonságok inicializálóiban található. A példánytagok hibaként jelennének meg ezeken a helyeken, de az elsődleges konstruktor paraméterei hatókörben és használhatóak lennének, és árnyéktagok lennének. A statikus tagok is használhatók.
Figyelmeztetést kell létrehozni, ha az elsődleges konstruktor egyik paramétere nem olvasható.
A szerkezetpéldány-konstruktorokra vonatkozó határozott hozzárendelési szabályok a rekordstruktúra elsődleges konstruktorára vonatkoznak. Például a következő hibaüzenet jelenik meg:
record struct Pos(int X) // def assignment error in primary constructor { private int x; public int X { get { return x; } set { x = value; } } = X; }
16.4.4.3 Tulajdonságok
A kifejezetten deklarált példánymezővel azonos nevű és típusú delimited_parameter_list minden paraméterére az alklám többi része nem vonatkozik.
Egy delimited_parameter_list minden rekordstrukturálási paraméteréhez tartozik egy megfelelő nyilvánostulajdon-tag, akinek a nevét és típusát az értékparaméter-deklarációból veszi.
Rekordstruktúra esetén:
egy nyilvános
getésinitegy automatikus tulajdonság jön létre, ha a rekordstruktúra módosítóval rendelkezikreadonly,getéssetellenkező esetben. Mindkét készletkiegészítő (setésinit) "egyezőnek" minősül. A felhasználó tehát csak init-only tulajdonságot deklarálhat egy szintetizált mutable helyett.Egy egyező típusú öröklött
abstracttulajdonság felül van bírálva.Nem jön létre automatikus tulajdonság, ha a rekordstruktúra egy várt névvel és típussal rendelkező példánymezővel rendelkezik.
Hiba, ha az örökölt tulajdonság nem rendelkezik
publicgetésset/inittartozékokkal rendelkezik.Hiba, ha az örökölt tulajdonság vagy mező rejtett.
Az automatikus tulajdonság inicializálása a megfelelő elsődleges konstruktorparaméter értékére történik.
Az attribútumok a szintetizált automatikus tulajdonságra és annak háttérmezőjére alkalmazhatók a megfelelő rekordstruktúraparaméterre szintaktikailag alkalmazott attribútumok használatával
property:vagyfield:céljaival.
16.4.4.4 Dekonstruktúra
A pozíciórekordstruktúra legalább egy paraméterrel szintetizálja a nyilvános void-returning instance metódust, amelyet out paraméterdeklarációval hívunk Deconstruct meg az elsődleges konstruktor-deklaráció minden paraméteréhez. Minden paraméter Deconstruct típusa megegyezik az elsődleges konstruktor-deklaráció megfelelő paraméterével. A metódus törzse hozzárendeli a Deconstruct metódus minden paraméterét ahhoz az értékhez, amely egy példánytag hozzáféréséből származik egy azonos nevű taghoz.
Ha a törzsben elért példánytagok nem tartalmaznak olyan tulajdonságot, amely nemreadonlyget tartozék, akkor a szintetizált Deconstruct metódus a readonly.
A metódus explicit módon deklarálható. Hiba, ha az explicit deklaráció nem felel meg a várt aláírásnak vagy akadálymentességnek, vagy statikus.
16.5 Osztály- és szerkezetkülönbségek
16.5.1 Általános
A szerkezetek számos fontos módon különböznek az osztályoktól:
- A szerkezetek értéktípusok (16.5.2. §).
- Minden szerkezettípus implicit módon örököl az osztálytól
System.ValueType(16.5.3. §). - Egy szerkezet típusú változóhoz való hozzárendelés létrehozza a hozzárendelt érték másolatát (16.5.4. §).
- A szerkezetek alapértelmezett értéke az az érték, amely az összes mező alapértelmezett értékére való beállításával jön létre (16.5.5.5. §).
- A boxing és a unboxing műveletek egy szerkezettípus és bizonyos referenciatípusok közötti konvertálásra szolgálnak (16.5.6. §).
- A tagokon
thisbelül más a jelentésük (16.5.7. §). - A szerkezetek nem deklarálhatnak véglegesítőt.
- Az eseménydeklarációk, a tulajdonságdeklarációk, a tulajdonságkiegészítők, az indexelő deklarációk és a metódusdeklarációk esetében engedélyezett a módosító
readonly, míg az osztályokban ezeknél a tagtípusoknál ez általánosan nem engedélyezett.
16.5.2 Értékszemantika
A szerkezetek értéktípusok (8.3.§), és azt mondják, hogy érték szemantikával rendelkeznek. Az osztályok viszont referenciatípusok (8.2.§), és azt mondják, hogy referencia szemantikával rendelkeznek.
A szerkezet típusú változók közvetlenül tartalmazzák a szerkezet adatait, míg egy osztálytípus változója az adatokat tartalmazó objektumra mutató hivatkozást tartalmaz. Ha egy struktúra B egy példány típusú A mezőt tartalmaz, és A egy strukturálási típus, akkor fordítási időhiba A , amely a B függvénytől függ, vagy egy olyan típus, amelyből Blétrejön. A struct Xközvetlenül attól függ, egy struct Y, ha XYtípusú példánymezőt tartalmaz. A definíció alapján a szerkezetek teljes halmaza, amelytől a szerkezet függ, a közvetlen kapcsolat tranzitív lezárása.
Példa:
struct Node { int data; Node next; // error, Node directly depends on itself }hiba, mert
Nodeegy saját típusú példánymezőt tartalmaz. Egy másik példastruct A { B b; } struct B { C c; } struct C { A a; }hiba, mert az egyes típusok
AB, ésCegymástól függnek.záró példa
Osztályokkal két változó hivatkozhat ugyanarra az objektumra, és így az egyik változón végzett műveletek hatással lehetnek a másik változó által hivatkozott objektumra. A szerkezetek esetében a változók mindegyike saját adatmásolattal rendelkezik (kivéve a hivatkozási paramétereket), és az egyiken végzett műveletek nem befolyásolhatják a másikat. Továbbá, kivéve, ha explicit módon null értékű (8.3.12. §), a szerkezettípus értékei nem lehetnek null.
Megjegyzés: Ha egy szerkezet hivatkozástípusú mezőt tartalmaz, akkor a hivatkozott objektum tartalmát más műveletek is módosíthatják. Azonban maga a mező értéke, vagyis az objektum, amelyre hivatkozik, nem módosítható egy másik strukturálási érték mutációja révén. végjegyzet
Példa: Tekintettel a következőkre
struct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { static void Main() { Point a = new Point(10, 10); Point b = a; a.x = 100; Console.WriteLine(b.x); } }a kimenet a következő
10: . A hozzárendelésabaz érték másolatát hozza létre, ésbígy a hozzárendelésa.xnem érinti. HaPointehelyett osztályként lett deklarálva, a kimenet azért lett100volna, mertabugyanarra az objektumra hivatkozna.záró példa
16.5.3 Öröklés
Minden struktúratípus implicit módon örököl az osztálytól System.ValueType, amely viszont az osztálytól objectöröklődik. A szerkezetdeklarációk megadhatják a implementált felületek listáját, de a szerkezetdeklarációk nem adhatnak meg alaposztályt.
A szerkezettípusok soha nem absztraktak, és mindig implicit módon vannak lezárva. A abstract szerkezetnyilatkozatban ezért a módosítók és sealed módosítók nem engedélyezettek.
Mivel az öröklés nem támogatott a szerkezetek esetében, a tag deklarált akadálymentessége nem lehet protected, private protectedvagy protected internal.
A szerkezetben lévő függvénytagok nem lehetnek absztraktak vagy virtuálisak, és a override módosító csak az örökölt System.ValueTypemetódusokat bírálhatja felül.
16.5.4 Hozzárendelés
Egy szerkezet típusú változóhoz való hozzárendelés létrehozza a hozzárendelt érték másolatát. Ez eltér az osztálytípus változóihoz való hozzárendeléstől, amely a hivatkozást másolja, de a hivatkozás által azonosított objektumot nem.
A hozzárendeléshez hasonlóan, ha egy szerkezetet értékparaméterként ad át, vagy függvénytag eredményeként ad vissza, létrejön a szerkezet egy példánya. A struktúra egy függvénytagra mutató hivatkozással továbbítható egy by-reference paraméter használatával.
Ha egy szerkezet tulajdonsága vagy indexelője egy hozzárendelés célja, a tulajdonsághoz vagy indexelőhöz való hozzáféréshez társított példánykifejezést változóként kell besorolni. Ha a példánykifejezés értékként van besorolva, fordítási időhiba lép fel. Ezt a 12.24.2.
16.5.5 Alapértelmezett értékek
A 9.3. §-ban leírtak szerint számos változó automatikusan inicializálódik az alapértelmezett értékre a létrehozásukkor. Az osztálytípusok és más referenciatípusok változói esetében ez az alapértelmezett érték.null Mivel azonban a szerkezetek olyan értéktípusok, amelyek nem lehetnek null, a szerkezetek alapértelmezett értéke az az érték, amelyet az összes értéktípus mező alapértelmezett értékére és az összes referenciatípus-mezőre nullvaló beállításával állítanak elő.
Példa: A fenti deklarált szerkezetre hivatkozva
Pointa példaPoint[] a = new Point[100];A tömb minden egyes
Pointelemét inicializálja a létrehozott értékre, és nullára állítja axymezőket.záró példa
A szerkezet alapértelmezett értéke megegyezik a szerkezet alapértelmezett konstruktorának visszaadott értékével (8.3.3. §). Ha egy szerkezet nem deklarál explicit paraméter nélküli példánykonstruktort, az alapértelmezett konstruktor szintetizálva lesz, és mindig azt az értéket adja vissza, amely az összes mező alapértelmezett értékre állításából ered. A default kifejezés mindig létrehozza a nulla inicializált alapértelmezett értéket, még akkor is, ha egy szerkezet explicit paraméter nélküli példánykonstruktort deklarál (16.4.9. §).
Megjegyzés: A szerkezeteket úgy kell megtervezni, hogy az alapértelmezett inicializálási állapotot érvényes állapotnak tekintsék. A példában
struct KeyValuePair { string key; string value; public KeyValuePair(string key, string value) { if (key == null || value == null) { throw new ArgumentException(); } this.key = key; this.value = value; } }a felhasználó által definiált példánykonstruktor csak akkor véd az értékek ellen
null, ha explicit módon hívják meg. Azokban az esetekben, amikor egyKeyValuePairváltozó alapértelmezett érték inicializálása történik, a mezők éskeyavaluemezők leszneknull, és a szerkezetnek készen kell állnia az állapot kezelésére.végjegyzet
16.5.6 Boxing and unboxing
Egy osztálytípus értéke átalakítható típussá object vagy interfésztípussá, amelyet az osztály egyszerűen a hivatkozás fordításkor egy másik típusként kezel. Hasonlóképpen, egy típus object vagy egy interfésztípus értéke a hivatkozás módosítása nélkül is átalakítható osztálytípussá (ebben az esetben természetesen futásidejű típusellenőrzésre van szükség).
Mivel a szerkezetek nem referenciatípusok, ezeket a műveleteket másképpen implementáljuk a szerkezettípusokhoz. Ha egy strukturálási típus értékét átalakítják bizonyos referenciatípusokká (a 10.2.9. §-ban meghatározottak szerint), boxing művelet történik. Hasonlóképpen, ha bizonyos referenciatípusok (a 10.3.7. §-ban meghatározottak szerint) értékét visszaalakítjuk egy strukturálási típusra, a rendszer lezáró műveletet fog végrehajtani. Az osztálytípusok ugyanazon műveleteitől lényeges különbség, hogy a dobozolás és a kicsomagolás átmásolja a strukturálási értéket a dobozos példányba vagy azon kívülre.
Megjegyzés: Így egy boxing vagy unboxing művelet után a beérkezett üzenetek
structmódosításai nem jelennek meg a dobozosstruct. végjegyzet
A dobozolással és a dobozolással kapcsolatos további részletekért lásd a 10.2.9 . és a 10.3.7.
16.5.7 Ennek jelentése
this A szerkezet jelentése eltér az osztály jelentésétől this a 12.8.14. Ha egy strukturálási típus felülbírál egy virtuális metódust, amely System.ValueTypeEqualsa virtuális metódus meghívásától öröklődik GetHashCode a strukturálási típus egy példányán keresztül, ToStringnem okoz boxolást. Ez akkor is igaz, ha a szerkezet típusparaméterként van használva, és a meghívás a típusparaméter-típus egy példányán keresztül történik.
Példa:
struct Counter { int value; public override string ToString() { value++; return value.ToString(); } } class Program { static void Test<T>() where T : new() { T x = new T(); Console.WriteLine(x.ToString()); Console.WriteLine(x.ToString()); Console.WriteLine(x.ToString()); } static void Main() => Test<Counter>(); }A program kimenete:
1 2 3Bár ez rossz stílust
ToString, hogy mellékhatások, a példa azt mutatja, hogy nem történt boxing a három meghívást ax.ToString().záró példa
Hasonlóképpen, a boxing soha nem történik implicit módon, amikor egy tagot korlátozott típusparaméteren ér el, amikor a tagot az értéktípuson belül implementálják. Tegyük fel például, hogy egy interfész ICounter tartalmaz egy metódust Increment, amely egy érték módosítására használható. Ha ICounter kényszerként használják, a metódus implementációját a Increment rendszer a meghívott változóra Increment való hivatkozással hívja meg, nem pedig dobozos másolattal.
Példa:
interface ICounter { void Increment(); } struct Counter : ICounter { int value; public override string ToString() => value.ToString(); void ICounter.Increment() => value++; } class Program { static void Test<T>() where T : ICounter, new() { T x = new T(); Console.WriteLine(x); x.Increment(); // Modify x Console.WriteLine(x); ((ICounter)x).Increment(); // Modify boxed copy of x Console.WriteLine(x); } static void Main() => Test<Counter>(); }Az első hívás, amely
Incrementmódosítja a változóxértékét. Ez nem egyezik meg a második hívássalIncrement, amely módosítja a dobozos másolatxértékét. Így a program kimenete a következő:0 1 1záró példa
16.5.8 Mező inicializálók
A 16.5.5. §-ban leírtak szerint a szerkezet alapértelmezett értéke az az érték, amely abból ered, hogy az összes értéktípusmezőt az alapértelmezett értékre állítja, és az összes referenciatípusmezőt az alapértelmezett értékre nullállítja. A szerkezet statikus és példánymezői változó inicializálókat tartalmazhatnak; példánymező-inicializáló esetén azonban legalább egy példánykonstruktort is deklarálni kell, vagy rekordszerkezet esetén delimited_parameter_list kell jelen lennie.
Példa:
Console.WriteLine($"Point is {new Point()}"); struct Point { public int x = 1; public int y = 1; public Point() { } public override string ToString() { return "(" + x + ", " + y + ")"; } }Point is (1, 1)záró példa
Ha a 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 a szerkezetpéldány-konstruktor rendelkezik egy this() konstruktor inicializálóval, amely az alapértelmezett paraméter nélküli konstruktort jelöli, a deklarált konstruktor implicit módon 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ásokat. A konstruktorhoz való belépés után az összes értéktípusmező az alapértelmezett értékre van állítva, és az összes referenciatípus-mező értéke null. Közvetlenül ezt követően a variable_initializers-nek megfelelő hozzárendelések sorozata lesz végrehajtva.
A field_declaration, amely közvetlenül egy struct_declaration belsejében van deklarálva, amely rendelkezik a struct_modifierreadonly-rel, rendelkeznie kell a field_modifierreadonly-rel.
16.5.9 Konstruktorok
A szerkezetek deklarálhatnak példánykonstruktorokat nulla vagy több paraméterrel. Ha egy struktúra nem rendelkezik explicit módon deklarált paraméter nélküli példánykonstruktorral, akkor a rendszer nyilvános akadálymentességgel szintetizálja az egyiket, amely mindig azt az értéket adja vissza, amely az összes értéktípusmező alapértelmezett értékére való beállításából és az összes referenciatípus-mező null beállításából ered (8.3.3.3. §). Ilyen esetben a példánymező-inicializálók figyelmen kívül lesznek hagyva a konstruktor végrehajtásakor.
A explicit módon deklarált paraméter nélküli példány-konstruktornak nyilvános akadálymentességgel kell rendelkeznie.
Példa: Tekintettel a következőkre:
using System; struct Point { int x = -1, y = -2; public Point(int x, int y) { this.x = x; this.y = y; } public override string ToString() { return "(" + x + ", " + y + ")"; } } class A { static void Main() { Console.WriteLine($"Point is {new Point()}"); Console.WriteLine($"Point is {new Point(0,0)}"); } }Point is (0, 0) Point is (0, 0)az utasítások nullával és
xyinicializálva is létrehoznak egyPointinicializálást, ami a paraméter nélküli példánykonstruktor hívása esetén meglepő lehet, mivel mindkét példánymező inicializálóval rendelkezik, de nem hajtja végre őket.záró példa
A szerkezetpéldány-konstruktorok nem tartalmazhatnak konstruktor inicializálót az űrlap base(argument_list), ahol a argument_list nem kötelező. A példánykonstruktor végrehajtása nem eredményezhet konstruktort a szerkezet alaptípusában System.ValueType.
A this szerkezetpéldány-konstruktor paramétere megfelel a struktúratípus kimeneti paraméterének. Ezért mindenképpen ki kell osztani (this. §) minden olyan helyen, ahol a konstruktor visszatér. Hasonlóképpen, a konstruktor törzsében nem olvasható (akár implicit módon is), mielőtt biztosan hozzárendelték volna.
Ha a szerkezetpéldány konstruktora konstruktor inicializálót ad meg, akkor az inicializáló ehhez a konstruktor törzse előtt bekövetkező határozott hozzárendelésnek minősül. Ezért maga a törzs nem rendelkezik inicializálási követelményekkel.
A példánymezőket (a mezők kivételével fixed ) mindenképpen olyan szerkezetpéldány-konstruktorokban kell hozzárendelni, amelyek nem rendelkeznek inicializálóval this() .
Példa: Fontolja meg az alábbi példánykonstruktor-implementációt:
struct Point { int x, y; public int X { set { x = value; } } public int Y { set { y = value; } } public Point(int x, int y) { X = x; // error, this is not yet definitely assigned Y = y; // error, this is not yet definitely assigned } }Egyetlen példányfüggvény-tag sem hívható meg (beleértve a tulajdonságokhoz
XésYa készlethez tartozó tartozékokat is) mindaddig, amíg a felépítendő szerkezet összes mezője biztosan ki nem van rendelve. Vegye figyelembe azonban, hogy haPointa szerkezet helyett osztály lenne, akkor a példánykonstruktor implementációja engedélyezve lenne. Ez alól egyetlen kivétel van, amely automatikusan implementált tulajdonságokat tartalmaz (15.7.4. §). A határozott hozzárendelési szabályok (12.24.2. §) kifejezetten mentesítik az adott szerkezettípus egy példánykonstruktorán belüli automatikus tulajdonságának hozzárendelését: az ilyen hozzárendelés az automatikus tulajdonság rejtett háttérmezőjének határozott hozzárendelése. Így a következők engedélyezettek:struct Point { public int X { get; set; } public int Y { get; set; } public Point(int x, int y) { X = x; // allowed, definitely assigns backing field Y = y; // allowed, definitely assigns backing field } }záró példa]
16.5.10 Statikus konstruktorok
A szerkezetek statikus konstruktorai ugyanazokat a szabályokat követik, mint az osztályok esetében. A szerkezettípus statikus konstruktorának végrehajtását az alábbi események közül az első aktiválja, amely egy alkalmazástartományon belül következik be:
- A szerkezettípus statikus tagjára hivatkozunk.
- A szerkezettípus explicit módon deklarált konstruktorát hívjuk meg.
Megjegyzés: A szerkezettípusok alapértelmezett értékeinek (16.5.5.5. §) létrehozása nem aktiválja a statikus konstruktort. (Erre példa egy tömb elemeinek kezdeti értéke.) végjegyzet
16.5.11 Tulajdonságok
A property_declaration (§15.7.1) egy struct_declaration példánytulajdonság esetén tartalmazhatja a property_modifierreadonly. A statikus tulajdonság azonban nem tartalmazhat ilyen módosító tulajdonságot.
Fordítási idejű hiba, ha egy példány-struktúra-változó állapotát próbáljuk módosítani ennek a struktúrának egy csak olvasható tulajdonságán keresztül.
Fordítási hibát okoz, ha egy automatikusan implementált tulajdonság readonly módosítóval rendelkezik, és egy set metódussal is.
Fordítási idejű hiba, ha egy readonly szerkezet automatikusan implementált tulajdonsága rendelkezik set hozzáférővel.
A szerkezeten belül readonly deklarált automatikusan implementált tulajdonságnak nem kell módosítóval rendelkeznie readonly , mivel a get tartozék implicit módon feltételezi, hogy olvasható.
Fordítási idejű hiba, ha egy readonly módosító szerepel magán a tulajdonságon, valamint annak get vagy set hozzáférőin.
Fordítási idejű hiba, ha egy tulajdonság minden hozzáférőjén readonly módosító van.
Megjegyzés: A hiba kijavításához helyezze át a módosítót az accessor-okból a tulajdonságba. végjegyzet
Tulajdonságkiegészítő kifejezés s.Pesetén:
Az automatikusan implementált tulajdonságok (§15.7.4) rejtett háttérmezőket használnak, amelyek csak a tulajdonság tartozékai számára érhetők el.
Megjegyzés: Ez a hozzáférési korlátozás azt jelenti, hogy az automatikusan implementált tulajdonságokat tartalmazó szerkezetek konstruktorainak gyakran explicit konstruktor-inicializálóra van szükségük, ahol egyébként nincs szükségük rá, hogy kielégítsék a függvénytagok meghívása vagy a konstruktor visszatérése előtt határozottan hozzárendelt összes mező követelményét. végjegyzet
16.5.12 Módszerek
A struct_declaration példánymetódusra vonatkozó method_declaration (15.6.1. §) tartalmazhatja a method_modifier. A statikus módszer azonban nem tartalmazhat ilyen módosító elemet.
Fordítási idejű hiba egy példánystruktúra állapotának módosítási kísérlete egy csak olvasható metódussal, amely az adott struktúrában van deklarálva.
Bár egy írásvédett metódus meghívhat egy testvér, nem írásvédett metódust, tulajdonság vagy indexelő lekérdező hozzáférőt, ez implicit másolat létrehozását eredményezi a this számára, mint védelmi intézkedést.
Az csak olvasható metódus meghívhat egy testvértulajdonságot vagy indexelő beállítót, amely csak olvasható. Ha egy testvértag kiegészítője nem explicit vagy implicit módon olvasható, fordítási hiba történik.
Egy részleges method_declaration metódusok mindegyike rendelkezik readonly módosítóval, vagy egyik sem rendelkezik.
16.5.13 Indexelők
Egy indexer_declaration (§15.9) példányindexelő egy struct_declaration-ban tartalmazhat indexer_modifierreadonly.
Fordítási időbeli hiba keletkezik, ha megpróbáljuk módosítani egy példánystruktúra változó állapotát az adott struktúrában deklarált, csak olvasható indexelőn keresztül.
Fordítási idejű hiba, ha egy readonly módosító található magán az indexelőn, vagy bármelyik get vagy set hozzáférőjén.
Az indexelő minden hozzáférőjén csak olvasható módosító esetén fordítási idejű hiba lép fel.
Megjegyzés: A hiba kijavításához helyezze át a módosítót a tartozékból az indexelőbe. végjegyzet
16.5.14 Események
Egy event_declaration (15.8.1. §), amely egy példányban lévő, nem mezőszerű eseményhez tartozik egy struct_declaration-ban, tartalmazhatja a event_modifierreadonly. A statikus esemény azonban nem tartalmazhat ilyen módosító elemet.
16.5.15 Biztonságos környezetkorlátozás
16.5.15.1 Általános
Fordításkor minden kifejezés egy olyan környezethez van társítva, amelyben az adott példány és az összes mező biztonságosan elérhető, a biztonságos környezete. A biztonságos környezet egy olyan környezet, amely egy kifejezést foglal magában, amely biztonságos ahhoz, hogy az érték megmenekülhessen.
Minden olyan kifejezés, amelynek fordítási ideje nem refstruktúra, a hívókörnyezet biztonságos kontextusával rendelkezik.
Egy default kifejezés bármilyen típushoz rendelkezik a hívókörnyezet biztonságos környezetével.
Minden olyan nem alapértelmezett kifejezés esetében, amelynek fordítási ideje refstruktúra, az alábbi szakaszokban meghatározott biztonságos környezettel rendelkezik.
A biztonságos környezet rögzíti, hogy egy érték mely környezetbe másolható. Ha egy biztonságos környezettel rendelkező kifejezéstől E1 egy biztonságos környezettel S1E2rendelkező kifejezéshez S2 hozzárendelést ad, az hiba, ha S2 szélesebb kontextusban van, mint S1.
A referenciaváltozókhoz (9.7.2.§) definiált ref-safe-context értékeknek három különböző biztonságos környezeti értéke van: deklarációblokk, függvénytag és hívókörnyezet. A kifejezések biztonságos környezete a következőképpen korlátozza a használatát:
- A visszautalási nyilatkozat
return e1esetében a hívó kontextusnake1kell lennie. - A hozzárendelés
e1 = e2esetében a biztonságos kontextusnake2legalább olyan szélesnek kell lennie, mint a biztonságos kontextusnake1.
Olyan metódushívás esetén, ha van egy ref típus vagy out argumentum ref struct (beleértve a fogadót is, kivéve, ha a típus readonly), a biztonságos környezettel S1együtt, akkor egyetlen argumentumnak (beleértve a fogadót is) lehet szűkebb biztonságos környezete, mint S1.
16.5.15.2 Paraméter biztonságos környezete
Egy refstruktúratípus paramétere, beleértve a this példánymetódus paraméterét is, a hívókörnyezet biztonságos kontextusával rendelkezik.
16.5.15.3 Helyi változók biztonságos környezete
A ref struct típusú helyi változók biztonságos környezettel rendelkeznek az alábbiak szerint:
- Ha a változó egy
foreachciklus iterációs változója, akkor a változó biztonságos környezete megegyezik aforeachhurok kifejezésének biztonságos kontextusával. - Ellenkező esetben, ha a változó deklarációja inicializálóval rendelkezik, akkor a változó biztonságos környezete megegyezik az inicializáló biztonságos környezetével.
- Ellenkező esetben a változó nem inicializálódik a deklarálási ponton, és a hívókörnyezet biztonságos kontextusával rendelkezik.
16.5.15.4 A mező biztonságos környezete
Egy olyan mezőre e.Fmutató hivatkozás, amelynek típusa F egy ref struct típus, egy olyan biztonságos környezettel rendelkezik, amely megegyezik a biztonságos környezetével e.
16.5.15.5 Operátorok
A felhasználó által definiált operátor alkalmazását metódushívásként kezeli a rendszer (16.5.15.6. §).
Az olyan operátorok esetében, amelyek értéket adnak, például e1 + e2 vagy c ? e1 : e2, az eredmény biztonságos kontextusa a legszűkebb környezet az operátor operandusainak biztonságos környezetei között. Ennek következtében egy olyan nemáris operátor esetében, amely értéket ad, például +eaz eredmény biztonságos kontextusa az operandus biztonságos kontextusa.
Megjegyzés: A feltételes operátor első operandusa egy
bool, így a biztonságos környezete a hívó-környezet. Ebből következik, hogy az eredményül kapott biztonságos környezet a második és harmadik operandus legszűkebb biztonságos kontextusa. végjegyzet
16.5.15.6 Metódus és tulajdonsághívás
Egy metódushívásból e1.M(e2, ...) vagy tulajdonsághívásból e.P eredő érték a legkisebb környezettel rendelkezik biztonságos környezettel:
- hívókörnyezet.
- Az összes argumentumkifejezés (beleértve a fogadót is) biztonságos környezete.
A tulajdonsághívást (vagy getset) a fenti szabályok az alapul szolgáló metódus meghívásának metódusaként kezelik.
16.5.15.7 stackalloc
A stackalloc-kifejezés eredménye a függvénytag biztonságos kontextusával rendelkezik.
16.5.15.8 Konstruktor-meghívások
A new konstruktort meghívó kifejezés ugyanazokat a szabályokat követi, mint egy metódushívás, amely úgy tekinthető, hogy a létrehozott típust adja vissza.
Emellett a biztonságos környezet a legkisebb az összes objektum inicializáló kifejezés argumentumainak és operandusainak biztonságos környezetei közül, rekurzív módon, ha van inicializáló.
Megjegyzés: Ezek a szabályok arra támaszkodnak
Span<T>, hogy nem rendelkezik konstruktorsal az alábbi űrlapon:public Span<T>(ref T p)Az ilyen konstruktorok a mezőként használt példányokat
Span<T>megkülönböztethetetlenekké teszik egyrefmezőből. A dokumentumban leírt biztonsági szabályok attól függnek, hogyrefmezők nem érvényesek c# vagy .NET. végjegyzet
ECMA C# draft specification