Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
16.1 Obecné
Struktury jsou podobné třídám, které představují datové struktury, které mohou obsahovat datové členy a členy funkce. Na rozdíl od tříd jsou však struktury typy hodnot a nevyžadují přidělení haldy. Proměnná struct typu přímo obsahuje data struct, zatímco proměnná typu třídy obsahuje odkaz na data, druhá známá jako objekt.
Poznámka: Struktury jsou zvláště užitečné pro malé datové struktury, které mají sémantiku hodnot. Komplexní čísla, body v souřadnicovém systému nebo páry klíč-hodnota ve slovníku jsou dobrými příklady struktur. Klíčem k těmto datovým strukturám je, že mají málo datových členů, že nevyžadují použití dědičnosti nebo sémantiky odkazu, spíše je možné je pohodlně implementovat pomocí sémantiky hodnot, kde přiřazení zkopíruje hodnotu místo odkazu. koncová poznámka
Jak je popsáno v §8.3.5, jednoduché typy poskytované jazykem C#, například int, doublea bool, jsou ve skutečnosti všechny typy struktur.
16.2 Deklarace struktury
16.2.1 Obecné
Struct_declaration je type_declaration (§14.8), která deklaruje novou strukturu:
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 ';'?
| ';'
;
Struct_declaration je určena buď pro strukturu bez záznamu, nebo pro strukturu záznamu.
Non_record_struct_declaration se skládá z volitelné sady atributů (§23), po níž následuje volitelná sada struct_modifiers (§16.2.2), po níž následuje volitelný ref modifikátor (§16.2.3), za kterým následuje volitelný částečný modifikátor (§15.2.7), za kterým následuje klíčové slovo struct a identifikátor, který pojmenuje strukturu, po ní následuje volitelná specifikace type_parameter_list (§15.2.3), za ním následuje volitelná specifikace struct_interfaces (§16.2.5), po níž následuje volitelná specifikace type_parameter_constraints-klauzule (§15.2.5), za kterou následuje struct_body (§16.2.6), případně následuje středník.
Record_struct_declaration se skládá z volitelné sady atributů (§23), za kterou následuje volitelná sada struct_modifiers (§16.2.2), za kterou následuje volitelný částečný modifikátor (§15.2.7), za kterým následuje klíčové slovo , následované klíčovým slovem recordstruct a identifikátorem, který pojmenuje strukturu, po ní následuje volitelná specifikace type_parameter_list (§15.2.3), za kterou následuje volitelný delimited_parameter_list specifikace (§15.2.1), po níž následuje volitelná specifikace struct_interfaces (§16.2.5), za kterou následuje volitelná specifikace type_parameter_constraints-klauzulí (§15.2.5), za kterou následuje record_struct_body.
Struct_declaration nesmí poskytovat type_parameter_constraints_clauses, pokud nenabídá ani type_parameter_list.
Struct_declaration, která poskytuje type_parameter_list, je obecná deklarace struktury. Kromě toho každá struktura vnořená uvnitř obecné deklarace třídy nebo obecná deklarace struktury je sama o sobě obecnou deklaraci struktury, protože argumenty typu pro obsahující typ musí být zadány k vytvoření konstruovaného typu (§8.4).
Non_record_struct_declaration, která obsahuje ref modifikátor, nesmí mít struct_interfaces část.
Record_struct_declaration, která má delimited_parameter_list deklaruje strukturu pozičního záznamu.
Maximálně jeden record_struct_declaration obsahující partial může poskytnout delimited_parameter_list.
Parametry v delimited_parameter_list nesmí obsahovat refani outthis modifikátory; inparams modifikátory jsou však povoleny.
Pro record_struct_declarationjsou record_struct_bodys {}{};a ; jsou ekvivalentní. Všechny naznačují, že jedinými členy jsou syntetizovány kompilátorem (§16.4).
16.2.2 Modifikátory struktury
Struct_declaration může volitelně obsahovat posloupnost struct_modifiers:
struct_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'readonly'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§24.2) je k dispozici pouze v nebezpečném kódu (§24).
Jedná se o chybu v době kompilace, aby se stejný modifikátor zobrazoval vícekrát v deklaraci struktury.
readonlyKromě toho mají modifikátory deklarace struktury stejný význam jako modifikátory deklarace třídy (§15.2.2).
readonly Modifikátor označuje, že struct_declaration deklaruje typ, jehož instance jsou neměnné.
Struktura jen pro čtení má následující omezení:
- Každé z jeho polí instance je rovněž deklarováno
readonly. - Nebude deklarovat žádné události podobné proměnným (§15.8.2).
Když je instance struktury jen pro čtení předána metodě, jeho this je považován za vstupní argument/parametr, který zakáže přístup k zápisu do všech polí instance (s výjimkou konstruktorů).
16.2.3 Modifikátor ref
ref Modifikátor označuje, že non_record_struct_declaration deklaruje typ, jehož instance jsou přiděleny v zásobníku provádění. Tyto typy se nazývají ref struct typy.
ref Modifikátor deklaruje, že instance mohou obsahovat pole typu ref-like, a nesmí být zkopírovány z bezpečného kontextu (§16.5.15). Pravidla pro určení bezpečného kontextu ref struktury jsou popsána v §16.5.15.
Jedná se o chybu v době kompilace, pokud se typ struktury ref používá v některém z následujících kontextů:
- Jako typ prvku pole.
- Jako deklarovaný typ pole třídy nebo struktury, která nemá
refmodifikátor. - Jako argument typu.
- Jako typ prvku n-tice.
- V asynchronní metodě.
- V iterátoru.
- Jako typ příjemce pro převod skupiny metod z metody instance na typ delegáta.
- Jako zachycená proměnná ve výrazu lambda nebo místní funkci.
Kromě toho platí následující omezení pro typ ref struct :
- Typ
ref structnesmí být boxován naSystem.ValueTypeneboSystem.Object. - Typ
ref structnesmí být deklarován k implementaci žádného rozhraní. - Metoda instance, která je deklarovaná v
objectnebo vSystem.ValueType, ale není přepsána v typuref struct, nesmí být volána na instanci tohoto typuref struct.
Poznámka: Nesmí
ref structdeklarovatasyncmetody instance ani v rámci metody instance použítyield returnaniyield breakpříkaz, protože implicitníthisparametr nelze použít v těchto kontextech. koncová poznámka
Tato omezení zajišťují, že proměnná typu ref struct neodkazuje na paměť zásobníku, která již není platná, nebo na proměnné, které již nejsou platné.
16.2.4 Částečný modifikátor
partial Modifikátor označuje, že tento struct_declaration je částečná deklarace typu. Více částečných deklarací struktury se stejným názvem v rámci uzavřené deklarace oboru názvů nebo typu je kombinováno tak, aby vytvořilo jednu deklaraci struktury podle pravidel uvedených v §15.2.7.
16.2.5 Rozhraní struktur
Deklarace struktury může obsahovat specifikaci struct_interfaces, v takovém případě se říká, že struktura přímo implementuje dané typy rozhraní. Pro konstruovaný typ struktury, včetně vnořeného typu deklarovaného v rámci obecné deklarace typu (§15.3.9.7), je každý typ implementovaného rozhraní získán nahrazením každého type_parameter v daném rozhraní, odpovídající type_argument konstruovaného typu.
struct_interfaces
: ':' interface_type_list
;
Zpracování rozhraní na více částech částečné deklarace struktury (§15.2.7) je dále popsáno v §15.2.4.3.
Implementace rozhraní jsou dále popsány v §19.6.
16.2.6 Tělo struktury
struct_body struktury definuje členy struktury.
struct_body
: '{' struct_member_declaration* '}'
;
16.3 Členy struktury
16.3.1 Obecné
Členové struktury se skládají z členů zavedených jeho struct_member_declaration, a členů zděděných z typu System.ValueType. Pro strukturu záznamu obsahuje sada členů také syntetizované členy generované kompilátorem (§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) je k dispozici pouze v nebezpečném kódu (§24).
Poznámka: Všechny druhy class_member_declarations s výjimkou finalizer_declaration jsou také struct_member_declarations. koncová poznámka
S výjimkou rozdílů uvedených v §16.5 se popisy členů třídy uvedené v §15.3 až §15.12 vztahují i na členy struktury.
Jedná se o chybu, že pole instance struktury záznamu má nebezpečný typ.
16.3.2 Členové readonly
Definice člena instance nebo přístupové objekty vlastnosti instance, indexeru nebo události, která obsahuje readonly modifikátor, má následující omezení:
- Parametr
thisjeref readonlyodkaz. - Člen nepřeřadí hodnotu
thisnebo pole instance příjemce. - Člen nepřeřadí hodnotu události podobné instanci (§15.8.2) příjemce.
- Pokud člen jen pro čtení vyvolá nečtený člen, musí být zkopírovaná struktura odkazovaná
thispomocí zapisovatelného odkazu prothisargument.
Poznámka: Pole instance obsahují skryté záložní pole použité pro automaticky implementované vlastnosti (§15.7.4). koncová poznámka
Příklad: Člen jen pro čtení může změnit stav objektu, na který odkazuje pole instance, i když člen jen pro čtení nemůže změnit přiřazení tohoto člena instance. Následující kód ukazuje opětovné přiřazení a úpravu pole instance:
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); } }Metoda
readonlyAddMessagemůže změnit stav seznamu zpráv. ČlenInitializeMessagesmůže vymazat a znovu inicializovat seznam zpráv. V případěAddMessagemodifikátorureadonlyje platný. V případěInitializeMessages, přidání modifikátorureadonlyje neplatné. konec příkladu
16.4 Syntetizované členy struktury záznamů
16.4.1 Obecné
V případě struktury záznamu jsou členy syntetizovány, pokud člen s podpisem "odpovídající" není deklarován v record_struct_body nebo přístupný konkrétní ne virtuální člen s "odpovídající" podpis je zděděný. Dva členové se považují za vyhovující, pokud mají stejný podpis nebo by se ve scénáři dědičnosti považovali za skrytí. (Viz Podpisy a přetížení §7.6.)
Syntetizované členy jsou popsány v následujících dílčích nákládách.
16.4.2 Členové rovnosti
Syntetizované členy rovnosti jsou podobné členům třídy záznamů (§15.16.2), s výjimkou chybějících , nulových EqualityContractkontrol nebo dědičnosti.
Struktura R záznamu implementuje System.IEquatable<R> a zahrnuje syntetizované přetížení silného Equals(R other)typu , která je veřejná, následujícím způsobem:
public readonly bool Equals(R other);
Tuto metodu lze deklarovat explicitně. Jedná se ale o chybu, pokud explicitní deklarace neodpovídá očekávanému podpisu nebo přístupnosti.
Pokud Equals(R other) je uživatelem definovaná (tj. syntetizována), ale GetHashCode není, vytvoří se upozornění.
Syntetizovaný se vrátítrue, pokud a pouze pokud pro každé pole fieldN instance v záznamu strukturuje hodnotu System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN), kde TN je typ pole, je true.Equals(R)
Struktura záznamu zahrnuje syntetizované == a != operátory ekvivalentní operátorům deklarovaným následujícím způsobem:
public static bool operator==(R r1, R r2) => r1.Equals(r2);
public static bool operator!=(R r1, R r2) => !(r1 == r2);
Metoda Equals volaná operátorem ==Equals(R other) je metoda zadaná výše. Operátor != deleguje na operátora == . Jedná se o chybu, pokud jsou operátory deklarovány explicitně.
Struktura záznamu zahrnuje syntetizované přepsání ekvivalentní metodě deklarované následujícím způsobem:
public override readonly bool Equals(object? obj);
Jedná se o chybu, pokud je přepsání deklarováno explicitně. Syntetizované přepsání vrátí other is R temp && Equals(temp) , kde R je struktura záznamu.
Struktura záznamu zahrnuje syntetizované přepsání ekvivalentní metodě deklarované následujícím způsobem:
public override readonly int GetHashCode();
Tato metoda může být deklarována explicitně.
Upozornění se oznámí, pokud jeden z Equals(R) nich a GetHashCode() je výslovně deklarován, ale druhá metoda není.
Syntetizované přepsání GetHashCode() vrátí výsledek kombinování hodnot System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN) pro každé pole fieldN instance s typem TNfieldN.int
Příklad: Zvažte následující strukturu záznamu:
record struct R1(T1 P1, T2 P2);Syntetizované členy rovnosti by tak vypadaly takto:
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));konec příkladu
16.4.3 Tisk členů
Struktura záznamu zahrnuje syntetizovanou metodu ekvivalentní následujícímu:
private bool PrintMembers(System.Text.StringBuilder builder);
Tato metoda provádí následující úlohy:
- Pro každý z tištěných členů struktury záznamu (nestatické veřejné pole a čitelné členy vlastností) připojí název
=člena následovaný "" a hodnotu člena oddělenou ", “, - Vrátí hodnotu true, pokud struktura záznamu má vytisknoutelné členy.
Pro člena, který má typ hodnoty, se jeho hodnota převede na řetězcové vyjádření.
Pokud tisknutelné členy záznamu neobsahují čitelný vlastnost s jinýmreadonlyget příslušenstvím, syntetizovaný PrintMembers je readonly. Není nutné, aby pole záznamu byla readonly určena pro metodu PrintMembersreadonly.
Metodu PrintMembers lze deklarovat explicitně. Jedná se ale o chybu, pokud explicitní deklarace neodpovídá očekávanému podpisu nebo přístupnosti.
Struktura záznamu zahrnuje syntetizovanou metodu ekvivalentní následujícímu:
public override string ToString();
Pokud je readonlymetoda struktury záznamu PrintMembers , syntetizovaná ToString() metoda musí být readonly.
Tuto metodu lze deklarovat explicitně. Jedná se o chybu, pokud explicitní deklarace neodpovídá očekávanému podpisu nebo přístupnosti.
Tato metoda provádí následující úlohy:
-
StringBuilderVytvoří instanci. - Připojí název struktury záznamu k tvůrci, za nímž následuje "
{", - Vyvolá metodu struktury
PrintMemberszáznamu, která jí dává tvůrce, následovaný "" pokud vrátil hodnotu true, - Připojí "
}", - Vrátí obsah tvůrce s
builder.ToString().
Příklad: Zvažte následující strukturu záznamu:
record struct R1(T1 P1, T2 P2);Pro tuto strukturu záznamů by syntetizované tiskové členy vypadaly přibližně takto:
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(); } }konec příkladu
16.4.4 Členové struktury pozičního záznamu
16.4.4.1 Obecné
Stejně jako poskytnutí členů popsaných v předchozích dílčích znacích, strukturách pozičních záznamů (§16.2.1) syntetizují další členy se stejnými podmínkami jako ostatní členy, jak je popsáno v následujících dílčích kkladech.
16.4.4.2 Primární konstruktor
Struktura záznamu má veřejný konstruktor, jehož podpis odpovídá parametrům hodnoty deklarace typu. Tomu se říká primární konstruktor pro typ. Jedná se o chybu, která má primární konstruktor a konstruktor se stejným podpisem, který již existuje ve struktuře. Pokud deklarace typu neobsahuje delimited_parameter_list, není generován žádný primární konstruktor.
record struct R1 { public R1() { } // OK } record struct R2() { public R2() { } // error: 'R2' already defines // a constructor with the same parameter types }
Deklarace polí instance pro strukturu záznamu mohou obsahovat inicializátory proměnných. Pokud neexistuje žádný primární konstruktor, inicializátory instance se spustí jako součást konstruktoru bez parametrů. V opačném případě primární konstruktor spustí inicializátory instance, které se zobrazí v těle record-struct-body.
Pokud má struktura záznamu primární konstruktor, každý uživatelem definovaný konstruktor musí mít explicitní this inicializátor konstruktoru, který volá primární konstruktor nebo explicitně deklarovaný konstruktor.
Parametry primárního konstruktoru i členů struktury záznamu jsou v oboru v rámci inicializátorů polí instance nebo vlastností. Členy instance by byly v těchto umístěních chybou, ale parametry primárního konstruktoru by byly v oboru a použitelné a stínové členy. Statické členy by také bylo možné použít.
Upozornění se vytvoří, pokud není přečtený parametr primárního konstruktoru.
Určitá pravidla přiřazení pro konstruktory instance struktury platí pro primární konstruktor struktury záznamů. Například následující chyba:
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 Vlastnosti
Pro každý parametr delimited_parameter_list , který má stejný název a typ jako explicitně deklarované pole instance, zbytek tohoto dílčího seznamu se nepoužije.
Pro každý parametr struktury záznamu delimited_parameter_list existuje odpovídající člen veřejné vlastnosti, jehož název a typ jsou převzaty z deklarace parametru hodnoty.
Pro strukturu záznamu:
Veřejná
getainitautomatická vlastnost je vytvořena, pokud struktura záznamureadonlymá modifikátor,getasetjinak. Oba druhy přístupových objektů sady (setainit) jsou považovány za "párování". Uživatel tedy může deklarovat vlastnost pouze inicializovat místo syntetizované mutable jedna.Zděděná
abstractvlastnost s odpovídajícím typem je přepsána.Pokud struktura záznamu obsahuje pole instance s očekávaným názvem a typem, není vytvořena žádná automatická vlastnost.
Jedná se o chybu, pokud zděděná vlastnost nemá
publicgetaset/initpřistupují.Jedná se o chybu, pokud je zděděná vlastnost nebo pole skryté.
Auto-vlastnost je inicializována na hodnotu odpovídajícího parametru primárního konstruktoru.
Atributy mohou být použity na syntetizované automatické vlastnosti a jeho backing pole pomocí
property:nebofield:cíle pro atributy syntakticky použité na odpovídající parametr struktury záznamu.
16.4.4.4 Dekonstrukce
Struktura pozičního záznamu s alespoň jedním parametrem syntetizuje veřejnou voidmetodu -returning instance volanou Deconstruct s deklarací výstupního parametru pro každý parametr deklarace primárního konstruktoru. Každý parametr má Deconstruct stejný typ jako odpovídající parametr deklarace primárního konstruktoru. Tělo metody přiřadí každý parametr Deconstruct metody k hodnotě z přístupu člena instance k členu člena stejného názvu.
Pokud členové instance, ke kterým se přistupuje v těle, nezahrnují vlastnost s nepřistupnýmreadonlyget objektem, syntetizovaná Deconstruct metoda je readonly.
Metodu lze deklarovat explicitně. Jedná se o chybu, pokud explicitní deklarace neodpovídá očekávanému podpisu nebo přístupnosti nebo je statická.
16.5 Rozdíly tříd a struktur
16.5.1 Obecné
Struktury se liší od tříd několika důležitými způsoby:
- Struktury jsou hodnotové typy (§16.5.2).
- Všechny typy struktur implicitně dědí z třídy
System.ValueType(§16.5.3). - Přiřazení proměnné typu struktury vytvoří kopii přiřazené hodnoty (§16.5.4).
- Výchozí hodnota struktury je hodnota vytvořená nastavením všech polí na výchozí hodnotu (§16.5.5).
- Boxing a unboxing operations are used to convert between a struct type and certain reference types (§16.5.6).
-
thisVýznam se liší v rámci členů struktury (§16.5.7). - Struktura nesmí deklarovat finalizátor.
- Deklarace událostí, deklarace vlastností, přístupové metody vlastnosti, deklarace indexeru a deklarace metody mohou mít modifikátor
readonly, i když to není obecně povoleno pro ty samé členy ve třídách.
16.5.2 Sémantika hodnot
Struktury jsou hodnotové typy (§8.3) a říká se, že mají sémantiku hodnot. Třídy jsou na druhou stranu referenčními typy (§8.2) a mají sémantiku odkazu.
Proměnná typu struktury přímo obsahuje data struktury, zatímco proměnná typu třídy obsahuje odkaz na objekt, který obsahuje data. Pokud struktura B obsahuje instanční pole typu A a A je typem struktury, je to chyba při kompilaci, pokud A závisí na B nebo na typu vytvořeném z B. Struktura Xpřímo závisí na struktury Y, pokud X obsahuje pole instance typu Y. Vzhledem k této definici je úplná množina struktur, na nichž závisí daná struktura, tranzitivní uzávěr relace přímé závislosti.
Příklad:
struct Node { int data; Node next; // error, Node directly depends on itself }je chyba, protože
Nodeobsahuje pole instance vlastního typu. Další příkladstruct A { B b; } struct B { C c; } struct C { A a; }je chyba, protože každý typ
A,BaCzávisí na sobě.konec příkladu
U tříd je možné, aby dvě proměnné odkazovaly na stejný objekt, a proto operace s jednou proměnnou ovlivnit objekt odkazovaný druhou proměnnou. U struktur mají proměnné vlastní kopii dat (s výjimkou parametrů podle odkazu) a není možné, aby operace s jednou z nich ovlivnily druhý. Kromě toho, pokud není explicitně nullable (§8.3.12), není možné, aby hodnoty typu struktury byly null.
Poznámka: Pokud struktura obsahuje pole typu odkazu, lze obsah odkazovaného objektu změnit jinými operacemi. Hodnota samotného pole, tj. objektu, na který odkazuje, však nelze změnit prostřednictvím mutaci jiné hodnoty struktury. koncová poznámka
Příklad: S ohledem na následující:
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); } }výstup je
10. Přiřazeníakbvytvoří kopii hodnoty, abproto není ovlivněno přiřazením ka.x. Kdyby bylPointmísto toho deklarován jako třída, výstup by byl100, protožeaabby odkazovaly na stejný objekt.konec příkladu
16.5.3 Dědičnost
Všechny typy struktur implicitně dědí z třídy System.ValueType, která následně dědí z třídy object. Deklarace struktury může určit seznam implementovaných rozhraní, ale není možné, aby deklarace struktury určila základní třídu.
Typy struktur nejsou nikdy abstraktní a vždy jsou implicitně zapečetěné. Modifikátory abstract a sealed proto nejsou povoleny v deklaraci struktury.
Vzhledem k tomu, že dědičnost není podporována pro struktury, deklarovaná přístupnost člena struktury nemůže být protected, private protectednebo protected internal.
Členy funkce ve struktuře nemohou být abstraktní nebo virtuální a override modifikátor je povolen pouze k přepsání metod zděděných z System.ValueType.
16.5.4 Přiřazení
Přiřazení proměnné typu struktury vytvoří kopii přiřazené hodnoty. Liší se od přiřazení k proměnné typu třídy, která kopíruje odkaz, ale nikoli objekt identifikovaný odkazem.
Podobně jako při přiřazení se při předání struktury jako hodnotový parametr nebo je vrácena jako výsledek funkčního člena vytvoří kopie struktury. Struktura může být předána odkazem na člen funkce pomocí parametru by-reference.
Je-li vlastnost nebo indexer struktury cílem přiřazení, výraz instance přidružený k vlastnosti nebo indexeru musí být klasifikován jako proměnná. Pokud je výraz instance klasifikován jako hodnota, dojde k chybě v době kompilace. Toto je podrobněji popsáno v §12.24.2.
16.5.5 Výchozí hodnoty
Jak je popsáno v §9.3, při vytváření se automaticky inicializuje několik druhů proměnných na jejich výchozí hodnotu. U proměnných typů tříd a jiných referenčních typů je nulltato výchozí hodnota . Vzhledem k tomu, že struktury jsou typy hodnot, které nemohou být null, výchozí hodnota struktury je hodnota vytvořená nastavením všech polí typu hodnoty na výchozí hodnotu a všechna pole odkazového typu na null.
Příklad: Odkazování na
Pointstrukturu deklarovanou výše, příkladPoint[] a = new Point[100];inicializuje každou
Pointz polí na hodnotu vytvořenou nastavenímxaypole na nulu.konec příkladu
Výchozí hodnota struktury odpovídá hodnotě vrácené výchozím konstruktorem struktury (§8.3.3). Pokud struktura nedeklaruje explicitní konstruktor instance bez parametrů, výchozí konstruktor je syntetizován a vždy vrátí hodnotu, která je výsledkem nastavení všech polí na výchozí hodnoty. Výraz default vždy vytvoří výchozí hodnotu s nulovou inicializovanou hodnotou, i když struktura deklaruje explicitní konstruktor instance bez parametrů (§16.4.9).
Poznámka: Struktury by měly být navrženy tak, aby zvážily výchozí inicializační stav platného stavu. V příkladu
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; } }konstruktor instance definovaný uživatelem chrání před
nullhodnotami pouze tam, kde je explicitně volána. V případech, kdy je proměnnáKeyValuePairpředmětem výchozí inicializace hodnot, budou polekeyavaluenulla struktura by měla být připravena na zpracování tohoto stavu.koncová poznámka
16.5.6 Boxing a unboxing
Hodnotu typu třídy lze převést na typ object nebo na typ rozhraní implementovaný třídou jednoduše tak, že v době kompilace považuje odkaz za jiný typ. Podobně je možné převést hodnotu typu object nebo hodnotu typu rozhraní zpět na typ třídy beze změny odkazu (v tomto případě je však nutná kontrola typu za běhu).
Vzhledem k tomu, že struktury nejsou odkazové typy, jsou tyto operace implementovány odlišně pro typy struktur. Pokud se hodnota typu struktury převede na určité referenční typy (jak je definováno v §10.2.9), dojde k operaci boxování. Podobně platí, že pokud je hodnota určitých referenčních typů (definovaná v §10.3.7) převedena zpět na typ struktury, provede se operace rozbalení. Klíčovým rozdílem od stejných operací u typů tříd je, že boxování a rozbalování kopíruje hodnotu struktury do boxované instance nebo z ní.
Poznámka: Po operaci boxingu nebo unboxingu se změny provedené v rozbaleném
structtedy neprojeví v boxovanémstruct. koncová poznámka
Další podrobnosti o balení a rozbalování naleznete v článcích §10.2.9 a §10.3.7.
16.5.7 Význam tohoto
Význam this struktury se liší od významu this třídy, jak je popsáno v §12.8.14. Pokud typ struktury přepíše virtuální metodu zděděnou z System.ValueType (například Equals, GetHashCode nebo ToString), vyvolání virtuální metody prostřednictvím instance typu struktury nezpůsobí boxování. To platí i v případě, že se struktura používá jako parametr typu a vyvolání probíhá prostřednictvím instance typu parametru.
Příklad:
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>(); }Výstupem programu je:
1 2 3I když je špatným stylem, aby
ToStringměl vedlejší účinky, příklad ukazuje, že k boxování nedošlo při třech vyvoláníchx.ToString().konec příkladu
Podobně při přístupu k členu omezujícím typ parametru, je-li tento člen implementován v rámci typu hodnoty, nikdy nedochází k implicitnímu boxingu. Předpokládejme například, že rozhraní ICounter obsahuje metodu Increment, kterou lze použít k úpravě hodnoty. Pokud se ICounter používá jako omezení, implementace metody Increment je volána s odkazem na proměnnou, na které byla volána Increment, nikdy boxovaná kopie.
Příklad:
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>(); }První volání
Incrementmodifikuje hodnotu v proměnnéx. Toto není ekvivalentem druhého voláníIncrement, které upraví hodnotu v zabalené kopiix. Výstupem programu je tedy:0 1 1konec příkladu
16.5.8 Inicializátory polí
Jak je popsáno v §16.5.5, výchozí hodnota struktury se skládá z hodnoty, která je výsledkem nastavení všech polí typu hodnoty na výchozí hodnotu a všech polí odkazového typu na null. Statická pole a pole instance struktury mohou obsahovat inicializátory proměnných; v případě inicializátoru pole instance však musí být deklarován alespoň jeden konstruktor instance nebo pro strukturu záznamu, musí být přítomna delimited_parameter_list .
Příklad:
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)konec příkladu
Pokud konstruktor instance struktury nemá žádný konstruktor inicializátor, tento konstruktor implicitně provádí inicializace určené variable_initializers polí instance deklarovaných ve své struktuře. To odpovídá posloupnosti přiřazení, která se spouští okamžitě po zadání do konstruktoru.
Pokud konstruktor instance struktury má this() konstruktor inicializátor, který představuje výchozí konstruktor bez parametrů, deklarovaný konstruktor implicitně vymaže všechna pole instance a provede inicializace určené variable_initializers polí instance deklarovaných ve své struktuře. Okamžitě po zadání do konstruktoru jsou všechna pole typu hodnoty nastavena na výchozí hodnotu a všechna pole typu odkazu jsou nastavena na null. Hned potom se provede posloupnost přiřazení odpovídající variable_initializers.
Field_declaration deklarované přímo uvnitř struct_declaration, který má struct_modifierreadonly, musí mít field_modifierreadonly.
16.5.9 – konstruktory
Struktura může deklarovat konstruktory instance s nulovými nebo více parametry. Pokud struktura nemá explicitně deklarovaný konstruktor instance bez parametrů, je syntetizován, s veřejnou přístupností, která vždy vrátí hodnotu, která má za následek nastavení všech polí typu hodnoty na výchozí hodnotu a všechna pole odkazového typu na null (§8.3.3). V takovém případě se všechny inicializátory polí instance při spuštění konstruktoru ignorují.
Explicitně deklarovaný konstruktor instance bez parametrů musí mít veřejnou přístupnost.
Příklad: S ohledem na následující:
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)příkazy vytvářejí inicializátory
Pointaxyinicializují se na nulu, což v případě volání konstruktoru instance bez parametrů může být překvapivá, protože obě pole instance mají inicializátory, ale nespouštějí se.konec příkladu
Konstruktor instance struktury nesmí obsahovat inicializátor konstruktoru formuláře base(argument_list), kde argument_list je nepovinný. Spuštění konstruktoru instance nesmí vést k provedení konstruktoru v základním typu System.ValueTypestruktury .
Parametr this konstruktoru instance struktury odpovídá výstupnímu parametru typu struktury.
this musí být jednoznačně přiřazen (§9.4) v každém místě, kde se konstruktor vrátí. Podobně jej nelze číst (ani implicitně) v těle konstruktoru, dokud nebude s jistotou přiřazený.
Pokud konstruktor instance struktury určuje inicializátor konstruktoru, tento inicializátor je považován za určité přiřazení k tomu, které nastane před tělem konstruktoru. Proto samotné tělo nemá žádné inicializační požadavky.
Pole instance (jiná než fixed pole) se rozhodně přiřazují v konstruktorech instancí struktury, které nemají this() inicializátor.
Příklad: Zvažte implementaci konstruktoru instance níže:
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 } }Nelze volat žádný člen funkce instance (včetně přístupových objektů sady pro vlastnosti
XaY) dokud nebudou jednoznačně přiřazena všechna pole struktury, která je vytvořena. Mějte však na paměti, že pokud byPointbyla třída místo struktury, implementace konstruktoru instanční by byla povolena. Existuje jedna výjimka, která zahrnuje automaticky implementované vlastnosti (§15.7.4). Určitá pravidla přiřazení (§12.24.2) výslovně vyloučila přiřazení k automatické vlastnosti typu struktury v rámci konstruktoru instance tohoto typu struktury: takové přiřazení je považováno za určité přiřazení skrytého záložního pole automatické vlastnosti. Proto je povoleno následující: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 } }konec příkladu]
16.5.10 Statické konstruktory
Statické konstruktory pro struktury se řídí většinou stejných pravidel jako u tříd. Spuštění statického konstruktoru pro typ struktury je aktivováno první z následujících událostí v rámci domény aplikace:
- Na statický člen typu struktury se odkazuje.
- Explicitně deklarovaný konstruktor strukturovaného typu je volán.
Poznámka: Vytvoření výchozích hodnot (§16.5.5) typů struktury neaktivuje statický konstruktor. (Příkladem je počáteční hodnota prvků v poli.) koncová poznámka
16.5.11 Vlastnosti
Deklarace vlastnosti (§15.7.1) pro vlastnost instance v deklarace struktury může obsahovat modifikátor vlastnostireadonly. Statická vlastnost však nesmí obsahovat tento modifikátor.
Jedná se o chybu v době kompilace, pokud se pokusíte změnit stav proměnné struktury instance prostřednictvím vlastnosti readonly deklarované v této struktuře.
Jedná se o chybu v době kompilace, pokud má automaticky implementovaná vlastnost modifikátor readonly a rovněž přístupový člen set.
Jedná se o chybu v době kompilace pro automaticky implementovanou vlastnost ve readonly struktuře, která má přístupový objekt set .
Automaticky implementovaná vlastnost deklarovaná uvnitř readonly struktury nemusí mít readonly modifikátor, protože jeho get příslušenství je implicitně považováno za jen pro čtení.
Jedná se o chybu v době kompilace mít readonly modifikátor u samotné vlastnosti i u kterékoliv z jejich get a set přístupových metod.
Jedná se o chybu v době kompilace, kdy vlastnost má u všech jeho přístupových objektů modifikátor jen pro čtení.
Poznámka: Chcete-li chybu opravit, přesuňte modifikátor z přístupových objektů do samotné vlastnosti. koncová poznámka
Pro výraz přistupování vlastností: s.P
- Jedná se o chybu v době kompilace, pokud
s.Pvyvolá přístupový objektMsady typuTpři procesu v §12.6.6.1 by vytvořil dočasnou kopiis. - Pokud
s.Pvyvolá přístupové objekty get typuT, proces v §12.6.6.1 se následuje, včetně vytvoření dočasné kopiesv případě potřeby.
Automaticky implementované vlastnosti (§15.7.4) používají skrytá doprovodná data, která jsou přístupná pouze přistupovým metodám k vlastnostem.
Poznámka: Toto omezení přístupu znamená, že konstruktory v strukturách obsahujících automaticky implementované vlastnosti často potřebují explicitní inicializátor konstruktoru, kde by jinak nepotřebují jeden, aby splňovaly požadavek všech polí, která jsou jednoznačně přiřazena před vyvoláním jakéhokoli člena funkce nebo vrátí konstruktor. koncová poznámka
16.5.12 Metody
Method_declaration (§15.6.1) pro instanční metodu v struct_declaration může obsahovat method_modifierreadonly. Statická metoda však nesmí tento modifikátor obsahovat.
Jedná se o chybu v době kompilace při pokusu o změnu stavu proměnné struktury instance prostřednictvím metody readonly deklarované v této struktuře.
I když metoda jen pro čtení může volat sesterskou, nesesterskou metodu, nebo vlastnost nebo indexer get accessor, výsledkem je vytvoření implicitní kopie this jako obranné opatření.
Metoda jen pro čtení může volat vlastnost na stejné stejné nebo indexer nastavit přístupový objekt, který je jen pro čtení. Pokud přístupový objekt sesterského člena není explicitně nebo implicitně pouze pro čtení, dojde k chybě kompilace.
Všechny deklarace částečné metody musí mít modifikátor, nebo žádná z nich ho nesmí mít.
16.5.13 Indexery
Indexovač_deklarace (§15.9) pro instanční indexovač ve struct_declaration může obsahovat indexer_modifierreadonly.
Jedná se o chybu v době kompilace při pokusu o změnu stavu proměnné struktury instance prostřednictvím indexeru jen pro čtení deklarovaného v této struktuře.
Jedná se o chybu v době kompilace, pokud je modifikátor readonly umístěn na samotném indexeru nebo na kterékoli z jeho přístupových metod get či set.
Jedná se o chybu v době kompilace, kdy indexer má u všech jeho přístupových objektů modifikátor jen pro čtení.
Poznámka: Chcete-li chybu opravit, přesuňte modifikátor z přístupových objektů do samotného indexeru. koncová poznámka
16.5.14 Události
Deklarace události (§15.8.1) pro instanci, ne jako pole, ve struct_declaration může obsahovat event_modifier. Statická událost však nesmí obsahovat tento modifikátor.
16.5.15 Bezpečné omezení kontextu
16.5.15.1 Obecné
V době kompilace je každý výraz přidružený k kontextu, kde tato instance a všechna její pole mohou být bezpečně přístupná, jeho bezpečný kontext. Bezpečný kontext je ten, ve kterém je výraz bezpečně uzavřen a kam může hodnota bezpečně uniknout.
Libovolný výraz, jehož typ kompilace není ref strukturou, má bezpečný kontext kontextu volajícího kontextu.
Výraz default pro libovolný typ má bezpečný kontext kontextu volajícího.
Pro jakýkoli nevýchozí výraz, jehož typ při kompilaci je ref struct, je bezpečný kontext definován v následujících částech.
Záznamy bezpečného kontextu, do kterých je možné zkopírovat kontextovou hodnotu. Při přiřazení z výrazu E1 s bezpečným kontextem S1k výrazu E2 s bezpečným kontextem S2se jedná o chybu, pokud S2 je širší kontext než S1.
Existují tři různé hodnoty bezpečného kontextu, stejné jako hodnoty kontextu ref-safe-context definované pro referenční proměnné (§9.7.2): blok deklarace, člen funkce a kontext volajícího. Bezpečný kontext výrazu omezuje jeho použití následujícím způsobem:
- V případě návratového prohlášení
return e1, musí být bezpečný kontexte1v kontextu volajícího. - Pro přiřazení
e1 = e2musí být bezpečný kontexte2alespoň tak široký, jako je bezpečný kontexte1.
Pro vyvolání metody, pokud existuje argument ref nebo out typu ref struct (včetně příjemce, pokud není typu readonly), s bezpečným kontextem S1, pak žádný argument (včetně příjemce) nesmí mít užší bezpečný kontext než S1.
16.5.15.2 Kontext bezpečného parametru
Parametr struktury typu ref, včetně parametru metody instance this, má bezpečný kontext volajícího.
16.5.15.3 Kontext bezpečné místní proměnné
Místní proměnná typu ref struct má bezpečný kontext následovně:
- Pokud je proměnná iterační proměnnou
foreachsmyčky, je bezpečný kontext proměnné stejný jako bezpečný kontext výrazuforeachsmyčky. - V opačném případě, pokud deklarace proměnné má inicializátor, pak je bezpečný kontext proměnné stejný jako bezpečný kontext tohoto inicializátoru.
- V opačném případě je proměnná neinicializována v okamžiku deklarace a má bezpečný kontext kontextu volajícího.
16.5.15.4 Kontext bezpečného pole
Odkaz na pole e.F, kde typ F je typ ref struktury, má bezpečný kontext, který je stejný jako bezpečný kontext e.
16.5.15.5 Operátory
Použití uživatelem definovaného operátoru se považuje za metodu vyvolání (§16.5.15.6).
Pro operátor, který poskytuje hodnotu, například e1 + e2 nebo c ? e1 : e2, bezpečný kontext výsledku je nejužší kontext mezi bezpečnými kontexty operandů operátoru. V důsledku toho je pro unární operátor, který poskytuje hodnotu, například +e, bezpečný kontext výsledku stejný jako bezpečný kontext operandu.
Poznámka: První operand podmíněného operátoru je
bool, takže jeho bezpečný kontext je kontext volajícího. Z toho vyplývá, že výsledný bezpečný kontext je nejužším bezpečným kontextem druhého a třetího operandu. koncová poznámka
16.5.15.6 Metoda a vyvolání vlastnosti
Hodnota, která je výsledkem vyvolání e1.M(e2, ...) metody nebo vyvolání e.P vlastnosti, má bezpečný kontext nejmenších z následujících kontextů:
- kontext volajícího.
- Bezpečný kontext všech výrazů argumentů (včetně příjemce).
Vyvolání vlastnosti (nebo getset) se považuje za metodu vyvolání základní metody výše uvedenými pravidly.
16.5.15.7 stackalloc
Výsledkem výrazu stackalloc je bezpečný kontext člena funkce.
16.5.15.8 Vyvolání konstruktoru
Výraz new , který vyvolá konstruktor, dodržuje stejná pravidla jako volání metody, která je považována za vrácení typu vytvořeného.
Navíc je bezpečný kontext nejmenší z bezpečných kontextů všech argumentů a operandů všech výrazů inicializátoru objektu rekurzivně, pokud je přítomen nějaký inicializátor.
Poznámka: Tato pravidla spoléhají na to, že
Span<T>nemá konstruktor následující podoby:public Span<T>(ref T p)Takový konstruktor vytváří instance
Span<T>používané jako pole nerozlišující odrefpole. Bezpečnostní pravidla popsaná v tomto dokumentu závisí na políchref, která nejsou platným konstruktorem v jazyce C# nebo .NET. koncová poznámka
ECMA C# draft specification