Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
16.1 Ogólne
Struktury są podobne do klas, w których reprezentują struktury danych, które mogą zawierać elementy członkowskie danych i składowe funkcji. Jednak w przeciwieństwie do klas struktury są typami wartościowymi i nie wymagają alokacji sterty. Zmienne typu struct bezpośrednio zawierają dane typu struct, natomiast zmienne typu klasowego zawierają odwołanie do danych, co jest znane jako obiekt.
Uwaga: struktury są szczególnie przydatne w przypadku małych struktur danych, które mają semantyka wartości. Liczby złożone, punkty w układzie współrzędnych lub pary klucz-wartość w słowniku są dobrymi przykładami struktur. Kluczem do tych struktur danych jest to, że mają niewiele członków danych, nie wymagają dziedziczenia ani semantyki odwołań, a można je wygodnie zaimplementować przy użyciu semantyki wartości, gdzie przypisanie kopiuje wartość zamiast odwołania. notatka końcowa
Zgodnie z opisem w §8.3.5 proste typy udostępniane przez język C#, takie jak int, doublei bool, są w rzeczywistości wszystkimi typami struktur.
Deklaracje struktury 16.2
16.2.1 Ogólne
Struct_declaration to type_declaration (§14.8), która deklaruje nową strukturę:
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 dotyczy struktury innej niż rekord lub struktury rekordów.
Non_record_struct_declaration składa się z opcjonalnego zestawu atrybutów (§23), a następnie opcjonalnego zestawu struct_modifiers (§16.2.2), a następnie opcjonalnego ref modyfikatora (§16.2.3), a następnie opcjonalnego modyfikatora częściowego (§15.2.7), a następnie słowa kluczowego struct i identyfikatora, który nazywa strukturę, oraz opcjonalną specyfikację type_parameter_list (§15.2.3), następnie opcjonalna specyfikacja struct_interfaces (§16.2.5), a następnie opcjonalna specyfikacja type_parameter_constraints-klauzul (§15.2.5), a następnie struct_body (§16.2.6), po której następuje średnik.
Record_struct_declaration składa się z opcjonalnego zestawu atrybutów (§23), a następnie opcjonalnego zestawu struct_modifiers (§16.2.2), a następnie opcjonalnego modyfikatora częściowego (§15.2.7), a następnie słowa kluczowego , a następnie słowa kluczowego recordstruct i identyfikatora, który nazywa strukturę, a następnie opcjonalną specyfikację type_parameter_list (§15.2.3), a następnie opcjonalną delimited_parameter_list specyfikacja (§15.2.1), a następnie opcjonalna specyfikacja struct_interfaces (§16.2.5), a następnie opcjonalna specyfikacja type_parameter_constraints-klauzul (§15.2.5), a następnie record_struct_body.
Struct_declaration nie dostarcza type_parameter_constraints_clauses, chyba że dostarcza również type_parameter_list.
Struct_declaration dostarczający type_parameter_list jest ogólną deklaracją struktury. Ponadto każda struktura zagnieżdżona wewnątrz deklaracji klasy ogólnej lub deklaracji struktury ogólnej jest samą deklaracją struktury ogólnej, ponieważ argumenty typu dla typu zawierającego należy podać w celu utworzenia skonstruowanego typu (§8.4).
Non_record_struct_declaration, który zawiera ref modyfikator, nie ma części struct_interfaces.
Record_struct_declaration o delimited_parameter_list deklaruje strukturę rekordów pozycyjnych.
Co najwyżej jeden record_struct_declaration zawierający partial może zapewnić delimited_parameter_list.
Parametry w delimited_parameter_list nie mają refmodyfikatorów ani outthis modyfikatorów; inparams jednak modyfikatory są dozwolone.
W przypadku record_struct_declarationrecord_struct_bodys {}, {};i ; są równoważne. Wszystkie wskazują, że jedynymi elementami członkowskimi są syntetyzowane przez kompilator (§16.4).
Modyfikatory struktury 16.2.2
struct_declaration może opcjonalnie zawierać sekwencję struct_modifier:
struct_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'readonly'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§24.2) jest dostępny tylko w niebezpiecznym kodzie (§24).
Jest to błąd czasu kompilacji dla tego samego modyfikatora, który pojawia się wiele razy w deklaracji struktury.
readonlyZ wyjątkiem , modyfikatory deklaracji struktury mają takie samo znaczenie jak te deklaracji klasy (§15.2.2).
Modyfikator readonly wskazuje, że struct_declaration deklaruje typ, którego wystąpienia są niezmienne.
Struktura readonly ma następujące ograniczenia:
- Każde z pól instancji powinno być również zadeklarowane
readonly. - Nie powinno deklarować żadnych zdarzeń przypominających pola (§15.8.2).
Gdy wystąpienie struktury readonly jest przekazywane do metody, jest ono this traktowane jak argument wejściowy lub parametr, co uniemożliwia zapis do dowolnych pól wystąpienia (z wyjątkiem przypadku konstruktorów).
16.2.3 Modyfikator ref
Modyfikator ref wskazuje, że non_record_struct_declaration deklaruje typ, którego wystąpienia są przydzielane na stosie wykonywania. Te typy są nazywane typami ref struct. Modyfikator ref deklaruje, że wystąpienia mogą zawierać pola podobne do ref i nie są kopiowane z kontekstu bezpiecznego (§16.5.15). Reguły określania bezpiecznego kontekstu struktury ref opisano w §16.5.15.
Jest to błąd czasu kompilacji, jeśli typ struktury ref jest używany w dowolnym z następujących kontekstów:
- Jako typ elementu tablicy.
- Jako zadeklarowany typ pola klasy lub struktury, która nie ma
refmodyfikatora. - Jako argument typu.
- Jako typ elementu krotki.
- W metodzie asynchronicznej.
- W iteratorze.
- Jako typ odbiorcy konwersji grupy metod z metody wystąpienia na typ delegata.
- Jako przechwycona zmienna w wyrażeniu lambda lub funkcji lokalnej.
Ponadto do typu mają zastosowanie ref struct następujące ograniczenia:
- Typ
ref structnie musi być w poluSystem.ValueTypelubSystem.Object. - Typ
ref structnie jest deklarowany w celu zaimplementowania żadnego interfejsu. - Metoda wystąpienia zadeklarowana w
objectlub wSystem.ValueType, ale nie przesłonięta w typieref struct, nie powinna być wywoływana z odbiornikiem tegoref structtypu.
Uwaga:
ref structnie może deklarować metod instancjiasyncani używać instrukcjiyield returnlubyield breakw metodzie instancji, ponieważ niejawny parametrthisnie może być używany w tych kontekstach. notatka końcowa
Te ograniczenia zapewniają, że zmienna typu nie odnosi się do pamięci stosu ref struct , która nie jest już prawidłowa, ani do zmiennych, które nie są już prawidłowe.
16.2.4 Modyfikator częściowy
Modyfikator partial wskazuje, że ten struct_declaration jest deklaracją typu częściowego. Wiele deklaracji częściowej struktury o tej samej nazwie w otaczającej przestrzeni nazw lub deklaracji typu łączą się w celu utworzenia jednej deklaracji struktury, zgodnie z regułami określonymi w §15.2.7.
Interfejsy struktury 16.2.5
Deklaracja struktury może zawierać specyfikację struct_interfaces , w tym przypadku struktura jest określana bezpośrednio do implementowania danych typów interfejsów. Dla skonstruowanego typu struktury, w tym typu zagnieżdżonego zadeklarowanego w deklaracji typu ogólnego (§15.3.9.7), każdy zaimplementowany typ interfejsu jest uzyskiwany przez podstawianie, dla każdego type_parameter w danym interfejsie, odpowiadających type_argument typu skonstruowanego.
struct_interfaces
: ':' interface_type_list
;
Obsługa interfejsów w wielu częściach części częściowej deklaracji struktury (§15.2.7) jest omówiona dalej w §15.2.4.3.
Implementacje interfejsu zostały omówione dalej w §19.6.
Treść struktury 16.2.6
struct_body definiuje elementy członkowskie struktury.
struct_body
: '{' struct_member_declaration* '}'
;
16.3, składowe struktury
16.3.1 Ogólne
Elementy członkowskie struktury składają się z elementów zdefiniowanych przez struct_member_declaration oraz elementów dziedziczonych z typu System.ValueType. W przypadku struktury rekordów zestaw składowy zawiera również syntetyzowane elementy członkowskie generowane przez kompilator (§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) jest dostępny tylko w niebezpiecznym kodzie (§24).
Uwaga: wszystkie rodzaje class_member_declaration z wyjątkiem finalizer_declaration są jednocześnie struct_member_declaration. notatka końcowa
Z wyjątkiem różnic odnotowanych w §16.5, opisy składowych klas podanych w §15.3 do §15.12 mają zastosowanie również do składowych struktury.
Jest to błąd dla pola wystąpienia struktury rekordów, aby mieć niebezpieczny typ.
16.3.2 Członkowie readonly
Definicja elementu członkowskiego wystąpienia lub metodę dostępu do właściwości wystąpienia, indeksatora lub zdarzenia zawierającego readonly modyfikator ma następujące ograniczenia:
- Parametr
thisjest odwołaniemref readonly. - Członek nie przypisuje ponownie wartości
thislub pola wystąpienia odbiornika. - Członek nie przypisuje ponownie wartości zdarzenia przypominającego pole wystąpienia (§15.8.2) odbiorcy.
- Jeśli element członkowski readonly wywołuje nieczytany element członkowski, należy skopiować strukturę,
thisaby użyć odwołania do zapisu dla argumentuthis.
Uwaga: Pola wystąpienia obejmują ukryte pole zapasowe używane do automatycznego implementowania właściwości (§15.7.4). notatka końcowa
Przykład: element członkowski tylko do odczytu może modyfikować stan obiektu, do którego odwołuje się pole wystąpienia, mimo że nie można ponownie przypisać tego elementu członkowskiego wystąpienia. Poniższy kod demonstruje ponowne przypisywanie i modyfikowanie pola wystąpienia:
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
readonlyAddMessagemoże zmienić stan listy komunikatów. ElementInitializeMessagesczłonkowski może wyczyścić i ponownie zainicjować listę komunikatów. W przypadkuAddMessagereadonlyprogramu modyfikator jest prawidłowy. W przypadkuInitializeMessagesmetody dodawaniereadonlymodyfikatora jest nieprawidłowe. przykład końcowy
16.4 Syntetyzowane składowe struktury rekordów
16.4.1 Ogólne
W przypadku struktury rekordów składowe są syntetyzowane, chyba że element członkowski z podpisem "pasującym" jest zadeklarowany w record_struct_body lub dostępny betonowy niewirtualny element członkowski z podpisem "pasującym" jest dziedziczony. Dwa elementy członkowskie są uważane za zgodne, jeśli mają ten sam podpis lub zostaną uznane za "ukrywanie" w scenariuszu dziedziczenia. (Zobacz Podpisy i przeciążenie §7.6).
Syntetyzowane elementy członkowskie są opisane w następujących podklasach.
16.4.2 Członkowie równości
Syntetyzowane składowe równości są podobne do tych dla klasy rekordów (§15.16.2), z wyjątkiem braku EqualityContract, sprawdzania wartości null lub dziedziczenia.
Struktura R rekordów implementuje System.IEquatable<R> i zawiera syntetyzowane przeciążenie silnie typizowane , Equals(R other)które jest publiczne w następujący sposób:
public readonly bool Equals(R other);
Tę metodę można zadeklarować jawnie. Jest to jednak błąd, jeśli jawna deklaracja nie jest zgodna z oczekiwanym podpisem lub ułatwieniami dostępu.
Jeśli Equals(R other) jest definiowany przez użytkownika (czyli nie syntetyzowany), ale GetHashCode nie, należy utworzyć ostrzeżenie.
Syntetyzowany Equals(R) zwraca wartość true , jeśli i tylko wtedy, gdy dla każdego pola fieldN wystąpienia w strukturę rekordu System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN)wartość , gdzie TN jest typem pola, jest true.
Struktura rekordów zawiera syntetyzowane == i != operatory równoważne operatorom zadeklarowanych w następujący sposób:
public static bool operator==(R r1, R r2) => r1.Equals(r2);
public static bool operator!=(R r1, R r2) => !(r1 == r2);
Metoda wywoływana Equals== przez Equals(R other) operator jest metodą określoną powyżej. Operator != deleguje do == operatora. Jest to błąd, jeśli operatory są zadeklarowane jawnie.
Struktura rekordów zawiera syntetyzowane zastąpienie równoważne metodzie zadeklarowanej w następujący sposób:
public override readonly bool Equals(object? obj);
Jest to błąd, jeśli zastąpienie jest zadeklarowane jawnie. Syntetyzowane przesłonięcia zwracają other is R temp && Equals(temp) , gdzie R jest strukturą rekordu.
Struktura rekordów zawiera syntetyzowane zastąpienie równoważne metodzie zadeklarowanej w następujący sposób:
public override readonly int GetHashCode();
Tę metodę można zadeklarować jawnie.
Ostrzeżenie jest zgłaszane, jeśli jeden z Equals(R) i GetHashCode() jest jawnie zadeklarowany, ale druga metoda nie jest.
Syntetyzowane przesłonięcia GetHashCode() zwraca int wynik połączenia wartości System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN) dla każdego pola fieldN wystąpienia z TN typem fieldN.
Przykład: Rozważ następującą strukturę rekordu:
record struct R1(T1 P1, T2 P2);W tym celu syntetyzowane elementy członkowskie równości będą wyglądać następująco:
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));przykład końcowy
16.4.3 Elementy członkowskie drukowania
Struktura rekordów zawiera metodę syntetyzowana równoważną następującym:
private bool PrintMembers(System.Text.StringBuilder builder);
Ta metoda wykonuje następujące zadania:
- Dla każdego elementu członkowskiego z możliwością drukowania struktury rekordu (niestatyczne pole publiczne i czytelne elementy członkowskie właściwości) dołącza nazwę tego elementu członkowskiego, po którym następuje "
=", a następnie wartość elementu członkowskiego oddzielona, “", - Zwraca wartość true, jeśli struktura rekordu ma drukowalne elementy członkowskie.
W przypadku elementu członkowskiego, który ma typ wartości, jego wartość jest konwertowana na reprezentację ciągu.
Jeśli elementy członkowskie, które można wydrukować rekordu, nie zawierają właściwości czytelnej z akcesoremreadonlyget, syntetyzowana PrintMembers jest .readonly Nie ma potrzeby, aby pola rekordu były readonlyPrintMembers dla metody readonly.
Metodę PrintMembers można zadeklarować jawnie. Jest to jednak błąd, jeśli jawna deklaracja nie jest zgodna z oczekiwanym podpisem lub ułatwieniami dostępu.
Struktura rekordów zawiera metodę syntetyzowana równoważną następującym:
public override string ToString();
Jeśli metoda struktury rekordu PrintMembers to readonly, metoda syntetyzowana ToString() ma wartość readonly.
Tę metodę można zadeklarować jawnie. Jest to błąd, jeśli jawna deklaracja nie jest zgodna z oczekiwanym podpisem lub ułatwieniami dostępu.
Ta metoda wykonuje następujące zadania:
-
StringBuilderTworzy wystąpienie, - Dołącza nazwę struktury rekordu do konstruktora, a następnie "
{", - Wywołuje metodę struktury
PrintMembersrekordu dając jej konstruktor, a następnie "", jeśli zwrócił wartość true, - Dołączanie "
}", - Zwraca zawartość konstruktora z elementem
builder.ToString().
Przykład: Rozważ następującą strukturę rekordu:
record struct R1(T1 P1, T2 P2);W przypadku tej struktury rekordów syntetyzowane elementy członkowskie drukowania będą wyglądać mniej więcej tak:
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(); } }przykład końcowy
16.4.4 Elementy członkowskie struktury rekordów pozycyjnych
16.4.4.1 Ogólne
Oprócz udostępniania składowych opisanych w poprzednich podkluczach, struktury rekordów pozycyjnych (§16.2.1) syntetyzować dodatkowe składowe z tymi samymi warunkami co pozostałe elementy członkowskie, zgodnie z opisem w poniższych podklasach.
16.4.4.2 Podstawowy konstruktor
Struktura rekordów ma publiczny konstruktor, którego podpis odpowiada parametrom wartości deklaracji typu. Jest to nazywane konstruktorem podstawowym dla typu. Jest to błąd, aby mieć konstruktor podstawowy i konstruktor z tym samym podpisem już obecnym w strukturę. Jeśli deklaracja typu nie zawiera delimited_parameter_list, nie jest generowany żaden konstruktor podstawowy.
record struct R1 { public R1() { } // OK } record struct R2() { public R2() { } // error: 'R2' already defines // a constructor with the same parameter types }
Deklaracje pól wystąpienia dla struktury rekordów mogą zawierać inicjatory zmiennych. Jeśli nie ma podstawowego konstruktora, inicjatory wystąpień są wykonywane jako część konstruktora bez parametrów. W przeciwnym razie w czasie wykonywania podstawowy konstruktor wykonuje inicjatory wystąpień pojawiające się w treści struct-body rekordu.
Jeśli struktura rekordów ma konstruktor podstawowy, każdy konstruktor zdefiniowany przez użytkownika musi mieć jawny this inicjator konstruktora, który wywołuje konstruktor podstawowy lub jawnie zadeklarowany konstruktor.
Parametry konstruktora podstawowego, a także elementy członkowskie struktury rekordów są w zakresie w inicjatorach pól lub właściwości wystąpienia. Elementy członkowskie wystąpienia byłyby błędem w tych lokalizacjach, ale parametry konstruktora podstawowego byłyby w zakresie i możliwe do użycia i byłyby elementami członkowskimi w tle. Statyczne elementy członkowskie byłyby również możliwe do użycia.
Ostrzeżenie jest generowane, jeśli parametr konstruktora podstawowego nie jest odczytywany.
Określone reguły przypisania konstruktorów wystąpień struktury mają zastosowanie do podstawowego konstruktora struktur rekordów. Na przykład następujący błąd:
record struct Pos(int X) // def assignment error in primary constructor { private int x; public int X { get { return x; } set { x = value; } } = X; }
Właściwości 16.4.4.3
Dla każdego parametru delimited_parameter_list o tej samej nazwie i typie co jawnie zadeklarowane pole wystąpienia, pozostała część tego podklasy nie ma zastosowania.
Dla każdego parametru struktury rekordu delimited_parameter_list istnieje odpowiedni element członkowski właściwości publicznej, którego nazwa i typ są pobierane z deklaracji parametru wartości.
W przypadku struktury rekordów:
Właściwość publiczna
getiinitautomatyczna jest tworzona, jeśli struktura rekordu mareadonlymodyfikator igetsetw inny sposób. Oba rodzaje zestawów metod dostępu (setiinit) są uważane za "pasujące". W związku z tym użytkownik może zadeklarować właściwość tylko do inicjowania zamiast syntetyzowanego modyfikowalnego.Właściwość dziedziczona
abstractz pasującym typem jest zastępowana.Nie jest tworzona żadna właściwość automatyczna, jeśli struktura rekordów ma pole wystąpienia o oczekiwanej nazwie i typie.
Jest to błąd, jeśli dziedziczona właściwość nie ma
publicgetwłaściwości iinitset/metody dostępu.Jest to błąd, jeśli dziedziczona właściwość lub pole jest ukryte.
Właściwość automatyczna jest inicjowana do wartości odpowiadającego podstawowego parametru konstruktora.
Atrybuty mogą być stosowane do syntetyzowanej właściwości auto-właściwości i jej pola zapasowego przy użyciu lub
property:field:elementów docelowych dla atrybutów syntaktycznie stosowanych do odpowiedniego parametru struktury rekordu.
16.4.4.4, dekonstrukcja
Struktura rekordów pozycyjnych z co najmniej jednym parametrem syntetyzuje publiczną voidmetodę wystąpienia zwracającą wywołaną Deconstruct deklaracją parametru out dla każdego parametru deklaracji konstruktora podstawowego. Każdy parametr ma Deconstruct ten sam typ co odpowiadający mu parametr podstawowej deklaracji konstruktora. Treść metody przypisuje każdy parametr metody Deconstruct do wartości z dostępu elementu członkowskiego wystąpienia do elementu członkowskiego o tej samej nazwie.
Jeśli członkowie wystąpienia, do których uzyskuje dostęp w treści, nie zawierają właściwości z akcesoremreadonlyget , metoda syntetyzowana Deconstruct to readonly.
Metodę można zadeklarować jawnie. Jest to błąd, jeśli jawna deklaracja nie jest zgodna z oczekiwanym podpisem lub ułatwieniami dostępu lub jest statyczna.
Różnice klas i struktur w wersji 16.5
16.5.1 Ogólne
Struktury różnią się od klas na kilka ważnych sposobów:
- Struktury są typami wartości (§16.5.2).
- Wszystkie typy struktur niejawnie dziedziczą z klasy
System.ValueType(§16.5.3). - Przypisanie do zmiennej typu struktury tworzy kopię przypisanej wartości (§16.5.4).
- Wartość domyślna struktury to wartość wygenerowana przez ustawienie wszystkich pól na wartość domyślną (§16.5.5).
- Operacje boksowania i rozpatrunia są używane do konwertowania między typem struktury a niektórymi typami referencyjnymi (§16.5.6).
- Znaczenie
thiselementu różni się w obrębie składowych struktury (§16.5.7). - Struktura nie może zadeklarować finalizatora.
- Deklaracje zdarzeń, deklaracje właściwości, metody dostępu do właściwości, deklaracje indeksatora i deklaracje metod mogą mieć modyfikator
readonly, podczas gdy nie jest to ogólnie dozwolone dla tych samych rodzajów składowych w klasach.
Semantyka wartości 16.5.2
Struktury są typami wartości (§8.3) i mówi się, że mają semantyka wartości. Klasy, z drugiej strony, są typami referencyjnymi (§8.2) i mówi się, że mają semantyka referencyjna.
Zmienna typu struktury zawiera bezpośrednio dane struktury, natomiast zmienna typu klasy zawiera odwołanie do obiektu zawierającego dane. Pl-PL: Gdy struktura B zawiera pole instancji typu A i A jest typem struktury, jest to błąd czasu kompilacji, aby A zależało od B lub typu skonstruowanego z B. Struktura Xbezpośrednio zależy odY struktury, jeśli X zawiera pole wystąpienia typu Y. Biorąc pod uwagę tę definicję, pełny zbiór struktur, od których dana struktura zależy, to przejściowe domknięcie relacji bezpośrednio zależy od.
Przykład:
struct Node { int data; Node next; // error, Node directly depends on itself }jest błędem, ponieważ
Nodezawiera pole wystąpienia własnego typu. Inny przykładstruct A { B b; } struct B { C c; } struct C { A a; }jest błędem, ponieważ każdy z typów
A,BiCzależy od siebie nawzajem.przykład końcowy
W przypadku klas możliwe jest, aby dwie zmienne odwoły się do tego samego obiektu, a tym samym możliwe, aby operacje na jednej zmiennej wpływały na obiekt, do którego odwołuje się druga zmienna. W przypadku struktur zmienne mają własną kopię danych (z wyjątkiem parametrów referencyjnych) i nie jest możliwe, aby operacje na jednym z nich miały wpływ na drugą. Ponadto, z wyjątkiem sytuacji, gdy jawnie dopuszczana jest wartość null (§8.3.12), wartości dla typu struktury nie mogą być null.
Uwaga: jeśli struktura zawiera pole typu odwołania, zawartość obiektu, do których odwołuje się odwołanie, może zostać zmieniona przez inne operacje. Jednak wartość samego pola, tj. obiekt, do którego się odwołuje, nie może zostać zmieniona przez mutację innej wartości struktury. notatka końcowa
Przykład: biorąc pod uwagę następujące
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); } }dane wyjściowe to
10. Przypisanieadobpowoduje utworzenie kopii wartości, i dlategobnie jest przez to zmieniony przy przypisaniu doa.x. Gdyby zamiast tego był zadeklarowany jako klasa, wynik byłbyPoint, ponieważ100iaodwoływałyby się do tego samego obiektu.przykład końcowy
Dziedziczenie 16.5.3
Wszystkie typy struktur niejawnie dziedziczą z klasy System.ValueType, która z kolei dziedziczy z klasy object. Deklaracja struktury może określać listę zaimplementowanych interfejsów, ale nie jest możliwe, aby deklaracja struktury określała klasę bazową.
Typy struktur nigdy nie są abstrakcyjne i zawsze są niejawnie zapieczętowane. Modyfikatory abstract i sealed nie są zatem dozwolone w deklaracji struktury.
Ponieważ dziedziczenie nie jest obsługiwane dla struktur, deklarowane ułatwienia dostępu elementu członkowskiego struktury nie mogą być protected, private protectedlub protected internal.
Członkowie funkcji w strukturze nie mogą być abstrakcyjni ani wirtualni, a modyfikator override może służyć tylko do zastępowania metod dziedziczonych z System.ValueType.
Przypisanie 16.5.4
Przypisanie do zmiennej typu struktury tworzy kopię przypisanej wartości. Różni się to od przypisania do zmiennej typu klasy, która kopiuje odwołanie, ale nie obiekt zidentyfikowany przez odwołanie.
Podobnie jak w przypadku przypisania, gdy struktura jest przekazywana jako parametr typu wartości lub zwracana w wyniku funkcji, tworzona jest kopia struktury. Struktura może zostać przekazana przez odwołanie do elementu członkowskiego funkcji przy użyciu parametru by-reference.
Gdy właściwość lub indeksator struktury jest celem przypisania, wyrażenie wystąpienia skojarzone z dostępem do właściwości lub indeksatora powinno być klasyfikowane jako zmienna. Jeśli wyrażenie wystąpienia zostanie sklasyfikowane jako wartość, pojawi się błąd podczas kompilacji. Opisano to szczegółowo w §12.24.2.
16.5.5 Wartości domyślne
Zgodnie z opisem w §9.3, kilka rodzajów zmiennych jest automatycznie inicjowanych do ich wartości domyślnej podczas ich tworzenia. W przypadku zmiennych typów klas i innych typów odwołań ta wartość domyślna to null. Jednak ponieważ struktury są typami wartości, których nie może być null, wartość domyślna struktury to wartość wygenerowana przez ustawienie wszystkich pól typu wartości na wartość domyślną, a wszystkie pola typu odwołania na nullwartość .
Przykład: odwołanie do struktury zadeklarowanej
Pointpowyżej, przykładPoint[] a = new Point[100];Inicjuje każdy element
Pointw tablicy, nadając mu wartość uzyskaną przez ustawienie pólxiyna zero.przykład końcowy
Wartość domyślna struktury odpowiada wartości zwracanej przez domyślny konstruktor struktury (§8.3.3). Gdy struktura nie deklaruje jawnego konstruktora wystąpienia bez parametrów, domyślny konstruktor jest syntetyzowany i zawsze zwraca wartość, która wynika z ustawienia wszystkich pól na ich wartości domyślne. Wyrażenie default zawsze generuje wartość domyślną zainicjowaną zero, nawet jeśli struktura deklaruje jawny konstruktor wystąpienia bez parametrów (§16.4.9).
Uwaga: Struktury powinny być zaprojektowane tak, aby uwzględnić domyślny stan inicjowania prawidłowym stanem. W przykładzie
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 instancji definiowany przez użytkownika chroni przed wartościami
nulltylko wtedy, gdy jest wywoływany w sposób jawny. W przypadkach, gdy zmiennaKeyValuePairpodlega inicjalizacji wartości domyślnej, polakeyivaluebędąnull, a struktura powinna być przygotowana do obsługi tego stanu.notatka końcowa
16.5.6 Boxing i rozpakowywanie
Wartość typu klasy można przekonwertować na typ object lub typ interfejsu implementowany przez klasę po prostu traktując odwołanie jako inny typ w czasie kompilacji. Podobnie wartość typu object lub wartość typu interfejsu można przekonwertować z powrotem na typ klasy bez zmiany odwołania (ale oczywiście w tym przypadku wymagane jest sprawdzenie typu w czasie wykonywania).
Ponieważ struktury nie są typami referencyjnymi, te operacje są implementowane inaczej dla typów struktur. Gdy wartość typu struktury jest konwertowana na określone typy odwołań (zgodnie z definicją w §10.2.9), odbywa się operacja boksowania. Podobnie, gdy wartość niektórych typów odwołań (zgodnie z definicją w §10.3.7) jest konwertowana z powrotem na typ struktury, odbywa się operacja wyodrębnienia. Kluczową różnicą między tymi samymi operacjami na typach klas jest to, że boxing i unboxing kopiują wartość struktury do lub z opakowanego wystąpienia.
Uwaga: W związku z tym po operacji boksowania lub rozpakowania zmiany wprowadzone w rozpakowanym
structnie są odzwierciedlane w zakapsułowanymstruct. notatka końcowa
Aby uzyskać więcej informacji na temat boxingu i unboxingu, zobacz §10.2.9 i §10.3.7.
16.5.7 Znaczenie tego
Znaczenie this struktury różni się od znaczenia this klasy, zgodnie z opisem w §12.8.14. Gdy typ struktury zastępuje metodę wirtualną odziedziczoną z System.ValueType (np. Equals, GetHashCode lub ToString), wywołanie tej metody wirtualnej za pośrednictwem instancji typu struktury nie powoduje wystąpienia boksowania. Dotyczy to nawet sytuacji, gdy struktura jest używana jako parametr typu, a wywołanie odbywa się poprzez instancję parametru typowego.
Przykład:
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>(); }Dane wyjściowe programu to:
1 2 3Chociaż jest to zły styl, aby
ToStringmiał skutki uboczne, w przykładzie pokazano, że w przypadku trzech wywołańx.ToString()nie wystąpiło żadne boksowanie.przykład końcowy
Podobnie, boxing nigdy nie występuje niejawnie podczas dostępu do elementu członkowskiego w parametrze typu ograniczonego, gdy element ten jest implementowany w typie wartości. Załóżmy na przykład, że interfejs ICounter zawiera metodę Increment, która może służyć do modyfikowania wartości. Jeśli ICounter jest używane jako ograniczenie, implementacja metody Increment jest wywoływana z odniesieniem do zmiennej, do której wywołano Increment, nigdy z zaboksowaną kopią.
Przykład:
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>(); }Pierwsze wywołanie
Incrementmodyfikuje wartość w zmiennejx. Nie jest to równoważne drugiemu wywołaniu funkcjiIncrement, które modyfikuje wartość w zamkniętej kopiix. W związku z tym dane wyjściowe programu to:0 1 1przykład końcowy
Inicjatory pól 16.5.8
Zgodnie z opisem w §16.5.5 wartość domyślna struktury składa się z wartości, która wynika z ustawienia wszystkich pól typu wartości na wartość domyślną, a wszystkie pola typu odwołania do null. Pola statyczne i pola wystąpień struktury mogą zawierać inicjatory zmiennych; jednak w przypadku inicjatora pola wystąpienia co najmniej jeden konstruktor wystąpienia jest również zadeklarowany lub w przypadku struktury rekordów, delimited_parameter_list jest obecny.
Przykład:
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)przykład końcowy
Gdy konstruktor wystąpienia struktury nie ma inicjatora konstruktora, ten konstruktor niejawnie wykonuje inicjacje określone przez variable_initializers pól wystąpienia zadeklarowanych w jego struktury. Odpowiada to sekwencji przypisań, które są wykonywane natychmiast po wejściu do konstruktora.
Gdy konstruktor wystąpienia struktury ma this() inicjator konstruktora, który reprezentuje domyślny konstruktor bez parametrów, zadeklarowany konstruktor niejawnie czyści wszystkie pola wystąpienia i wykonuje inicjalizacji określone przez variable_initializerpól wystąpienia zadeklarowanych w jego struktury. Natychmiast po wpisie do konstruktora wszystkie pola typu wartości są ustawione na ich wartość domyślną, a wszystkie pola typu odwołania są ustawione na null. Natychmiast po tym jest wykonywana sekwencja przypisań odpowiadających variable_initializers.
Field_declaration, zadeklarowany bezpośrednio w struct_declaration, posiadający struct_modifierreadonly, musi mieć field_modifierreadonly.
Konstruktory 16.5.9
Struktura może deklarować konstruktory wystąpień z zerowymi lub większą liczbie parametrów. Jeśli struktura nie ma jawnie zadeklarowanego konstruktora wystąpienia bez parametrów, jeden jest syntetyzowany z ułatwieniami dostępu publicznego, który zawsze zwraca wartość wynikającą z ustawienia wszystkich pól typu wartości na wartość domyślną i wszystkich pól typu odwołania do null (§8.3.3). W takim przypadku wszystkie inicjatory pól wystąpienia są ignorowane podczas wykonywania tego konstruktora.
Jawnie zadeklarowany konstruktor wystąpienia bez parametrów ma dostęp publiczny.
Przykład: biorąc pod uwagę następujące kwestie:
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)Instrukcje tworzą
Pointobiekt z wartością ixyinicjowane na zero, co w przypadku wywołania konstruktora wystąpienia bez parametrów może być zaskakujące, ponieważ oba pola wystąpienia mają inicjatory, ale nie są wykonywane.przykład końcowy
Konstruktor wystąpienia struktury nie może zawierać inicjalizatora konstruktora w formie base(argument_list), gdzie argument_list jest opcjonalny. Wykonanie konstruktora wystąpienia nie powoduje wykonania konstruktora w typie System.ValueTypepodstawowym struktury .
Parametr this konstruktora wystąpienia struktury odpowiada parametrowi wyjściowemu typu struktury. W związku z tym this musi być definitywnie przypisane (§9.4) w każdym miejscu, gdzie konstruktor zwraca. Podobnie nie można go odczytać (nawet niejawnie) w ciele konstruktora przed jednoznacznym przypisaniem.
Jeśli konstruktor wystąpienia struktury określa inicjator konstruktora, inicjator ten jest uważany za określone przypisanie do tego, który występuje przed treścią konstruktora. W związku z tym samo ciało nie ma wymagań inicjalizacji.
Pola wystąpienia (inne niż fixed pola) są zdecydowanie przypisywane w konstruktorach wystąpień struktury, które nie mają inicjatora this() .
Przykład: Rozważmy implementację konstruktora instancji poniżej:
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 } }Nie można wywołać funkcji członkowskiej instancji (w tym akcesorów set dla właściwości
XiY), dopóki wszystkie pola konstruowanej struktury nie zostaną jednoznacznie przypisane. Należy jednak pamiętać, że w przypadkuPointklasy zamiast struktury implementacja konstruktora wystąpienia byłaby dozwolona. Istnieje jeden wyjątek od tego i obejmuje on automatycznie zaimplementowane właściwości (§15.7.4). Określone reguły przypisania (§12.24.2) w szczególności wykluczają przypisanie do właściwości automatycznej typu struktury w konstruktorze wystąpienia tego typu struktury: takie przypisanie jest uważane za określone przypisanie ukrytego pola zapasowego właściwości automatycznej. W związku z tym dozwolone są następujące elementy: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 } }przykład końcowy]
16.5.10 Konstruktory statyczne
Konstruktory statyczne dla struktur są zgodne z większością tych samych reguł co w przypadku klas. Wykonanie konstruktora statycznego dla typu struktury jest wyzwalane przez pierwsze z następujących zdarzeń, które mają wystąpić w domenie aplikacji:
- Odwołano się do statycznej składowej typu struktury.
- Jawnie zadeklarowany konstruktor typu struktury jest wywoływany.
Uwaga: Tworzenie wartości domyślnych (§16.5.5) typów struktur nie powoduje wyzwolenia konstruktora statycznego. (Przykładem jest początkowa wartość elementów w tablicy). notatka końcowa
Właściwości 16.5.11
Deklaracja właściwości (§15.7.1) dla właściwości instancyjnej w strukturze może zawierać modyfikator_właściwościreadonly. Jednak właściwość statyczna nie zawiera tego modyfikatora.
Błąd czasu kompilacji występuje, gdy próbuje się zmodyfikować stan zmiennej struktury instancji przez właściwość tylko do odczytu zadeklarowaną w tej strukturze.
Jest to błąd czasu kompilacji, gdy automatycznie zaimplementowana właściwość z modyfikatorem readonly ma także akcesor set.
Jest to błąd czasu kompilacji dla automatycznie zaimplementowanej właściwości w strukturze readonly, aby miała akcesor set.
Automatycznie zaimplementowana właściwość zadeklarowana wewnątrz readonly struktury nie musi mieć readonly modyfikatora, ponieważ jej get akcesor jest domyślnie uznawany za tylko do odczytu.
Jest błędem czasu kompilacji, aby umieścić modyfikator readonly zarówno na samej właściwości, jak i na którymkolwiek z jej klientów get lub set.
Stanowi to błąd czasu kompilacji, gdy właściwość ma modyfikator readonly na wszystkich swoich akcesorach.
Uwaga: Aby naprawić błąd, przenieś modyfikator z metod dostępu do samej właściwości. notatka końcowa
W przypadku wyrażenia dostępu właściwości: s.P
- Jest to błąd czasu kompilacji, jeśli
s.Pwywołuje zestaw metod dostępuMtypuT, gdy proces w §12.6.6.1 utworzy tymczasową kopięs. - Jeśli
s.Pwywoła metodę pobierania typuT, następuje proces w §12.6.6.1 , w tym utworzenie tymczasowej kopiis, jeśli jest to wymagane.
Automatycznie zaimplementowane właściwości (§15.7.4) używają ukrytych pól pomocniczych, które są dostępne tylko dla metod dostępu do właściwości.
Uwaga: To ograniczenie dostępu oznacza, że konstruktory struktur zawierających automatycznie zaimplementowane właściwości często wymagają jawnego inicjatora konstruktora, nawet jeśli nie byłoby to konieczne w innych okolicznościach, aby spełnić wymóg, że wszystkie pola muszą być jednoznacznie przypisane przed wywołaniem dowolnego elementu członkowskiego funkcji lub zakończeniem się konstruktora. notatka końcowa
Metody 16.5.12
A method_declaration (§15.6.1) dla metody instancji w struct_declaration może zawierać method_modifierreadonly. Jednak metoda statyczna nie zawiera tego modyfikatora.
Jest to błąd czasu kompilacji, wynikający z próby modyfikacji stanu zmiennej struktury instancji za pomocą metody readonly zadeklarowanej w tej strukturze.
Mimo że metoda readonly może wywołać równorzędną metodę nie-readonly lub akcesor uzyskiwania właściwości albo indeksatora, prowadzi to do utworzenia niejawnej kopii this jako środka defensywnego.
Metoda readonly może wywołać równorzędną właściwość lub akcesor ustawiający indeksatora, który jest tylko do odczytu. Jeśli akcesor członka równorzędnego nie jest jawnie lub niejawnie tylko do odczytu, wystąpi błąd kompilacji.
Wszystkie method_declaration metody częściowe muszą mieć modyfikator readonly, lub żadna z nich nie może go mieć.
Indeksatory 16.5.13
Deklaracja indexer_declaration (§15.9) dla indeksatora wystąpienia w struct_declaration może zawierać indexer_modifierreadonly.
Jest to błąd czasu kompilacji, gdy próbuje się zmodyfikować stan zmiennej struktury instancji za pomocą indeksatora oznaczonego jako readonly zadeklarowanego w tej strukturze.
Jest to błąd czasu kompilacji, gdy modyfikator readonly jest zastosowany na samym indeksatorze, jak również na jednym z jego akcesorów get lub set.
Błąd czasu kompilacji występuje, gdy indeksator ma modyfikator readonly we wszystkich swoich akcesorach.
Uwaga: Aby naprawić błąd, przenieś modyfikator z metod dostępu do samego indeksatora. notatka końcowa
Zdarzenia 16.5.14
Na przykład event_declaration (§15.8.1) zdarzenie inne niż pole w struct_declaration może zawierać event_modifierreadonly. Jednak zdarzenie statyczne nie zawiera tego modyfikatora.
16.5.15 Bezpieczne ograniczenie kontekstu
16.5.15.1 Ogólne
W czasie kompilacji każde wyrażenie jest skojarzone z kontekstem, w którym to wystąpienie i wszystkie jego pola mogą być bezpiecznie dostępne, jego bezpieczny kontekst. Bezpieczny kontekst to kontekst otaczający wyrażenie, do którego wartości mogą bezpiecznie przechodzić.
Dowolne wyrażenie, którego typ czasu kompilacji nie jest ref struct, ma bezpieczny kontekst wywołującego.
Wyrażenie default dla dowolnego typu ma bezpieczny kontekst kontekstu wywołującego.
W przypadku dowolnego wyrażenia innego niż domyślne, którego typem czasu kompilacji jest struktura ref, ma bezpieczny kontekst zdefiniowany w poniższych sekcjach.
Rekordy bezpiecznego kontekstu, do których można skopiować wartość. Przypisanie z wyrażenia E1 z bezpiecznym kontekstem S1 do wyrażenia E2 z bezpiecznym kontekstem S2 jest błędem, jeśli S2 jest szerszym kontekstem niż S1.
Istnieją trzy różne wartości bezpiecznego kontekstu, takie same jak wartości kontekstu bezpiecznego ref zdefiniowane dla zmiennych referencyjnych (§9.7.2): blok-deklaracji, składowa funkcji i kontekst wywołującego. Bezpieczny kontekst wyrażenia ogranicza jego użycie w następujący sposób:
- W przypadku instrukcji return
return e1, kontekstem bezpiecznym dlae1powinien być kontekst wywołujący. - W przypadku przypisania
e1 = e2bezpieczny kontekste2musi być co najmniej tak szeroki, jak bezpieczny kontekste1.
W przypadku wywołania metody, jeśli istnieje argument ref lub out typu ref struct (w tym odbiornik, chyba że typem jest readonly), z bezpiecznym kontekstem S1, wówczas żaden argument (w tym odbiornik) nie może mieć węższego bezpiecznego kontekstu niż S1.
16.5.15.2 Kontekst bezpieczny parametru
Parametr typu struktury ref, w tym parametr this metody wystąpienia, ma bezpieczny kontekst wywołującego.
Bezpieczny kontekst zmiennej lokalnej 16.5.15.3
Lokalna zmienna typu struktury ref ma bezpieczny kontekst w następujący sposób:
- Jeśli zmienna jest zmienną
foreachiteracji pętli, bezpieczny kontekst zmiennej jest taki sam jak bezpieczny kontekstforeachwyrażenia pętli. - W przeciwnym razie, jeśli deklaracja zmiennej ma inicjator, bezpieczny kontekst zmiennej jest taki sam jak bezpieczny kontekst tego inicjatora.
- W przeciwnym razie zmienna jest niezainicjowana w momencie deklaracji i ma bezpieczny kontekst w kontekście wywołującego.
Kontekst bezpieczny pola 16.5.15.4
Odwołanie do pola e.F, gdzie typ F jest typem struktury ref, ma bezpieczny kontekst, który jest taki sam jak kontekst bezpieczny e.
Operatory 16.5.15.5
Zastosowanie operatora zdefiniowanego przez użytkownika jest traktowane jako wywołanie metody (§16.5.15.6).
Dla operatora, który zwraca wartość, taką jak e1 + e2 lub c ? e1 : e2, bezpieczny kontekst wyniku jest najwęższym kontekstem wśród bezpiecznych kontekstów operandów operatora. W konsekwencji dla operatora jednoargumentowego, który daje wartość, taką jak +e, bezpieczny kontekst wyniku jest bezpiecznym kontekstem operandu.
Uwaga: pierwszy operand operatora warunkowego to
bool, więc jego kontekst bezpieczny jest kontekstem wywołującym. Wynika z tego, że wynikowy kontekst bezpieczeństwa jest najwęższym kontekstem bezpieczeństwa drugiego i trzeciego operandu. notatka końcowa
16.5.15.6 Wywołanie metody i właściwości
Wartość wynikająca z wywołania metody e1.M(e2, ...) lub wywołania właściwości e.P jest bezpieczna w kontekście najmniejszego z następujących kontekstów.
- kontekst dzwoniącego
- Kontekst bezpieczeństwa wszystkich wyrażeń argumentów (w tym odbiorcy).
Wywołanie właściwości ( get lub set) jest traktowane jako wywołanie metody bazowej przez powyższe reguły.
16.5.15.7 stackalloc
Wynik wyrażenia stackalloc ma bezpieczny kontekst członka funkcji.
Wywołania konstruktora 16.5.15.8
Wyrażenie new, które wywołuje konstruktor, przestrzega tych samych reguł co wywołanie metody, uważane za zwracające typ, który jest tworzony.
Ponadto bezpieczny kontekst jest najmniejszym kontekstem bezpiecznym wszystkich argumentów i operandów wszystkich wyrażeń inicjatora obiektów, rekursywnie, jeśli istnieje jakikolwiek inicjator.
Uwaga: Te reguły opierają się na
Span<T>, który nie ma konstruktora w następującej formie:public Span<T>(ref T p)Taki konstruktor sprawia, że wystąpienia
Span<T>, używane jako pola, są nie do odróżnienia od polaref. Reguły bezpieczeństwa opisane w tym dokumencie zależą od pólref, które nie są prawidłową konstrukcją w języku C# lub .NET. notatka końcowa
ECMA C# draft specification