Struktury záznamů
Poznámka
Tento článek je specifikace funkce. Specifikace slouží jako návrhový dokument pro funkci. Zahrnuje navrhované změny specifikace spolu s informacemi potřebnými při návrhu a vývoji funkce. Tyto články se publikují, dokud nebudou navrhované změny specifikace finalizovány a začleněny do aktuální specifikace ECMA.
Mezi specifikací funkce a dokončenou implementací může docházet k nějakým nesrovnalostem. Tyto rozdíly jsou zachyceny v poznámkách ze schůzky návrhu jazyka (LDM) .
Další informace o procesu přijetí specifikací funkcí do jazyka C# najdete v článku o specifikacích .
Problém šampiona: https://github.com/dotnet/csharplang/issues/4334
Syntaxe struktury záznamu je následující:
record_struct_declaration
: attributes? struct_modifier* 'partial'? 'record' 'struct' identifier type_parameter_list?
parameter_list? struct_interfaces? type_parameter_constraints_clause* record_struct_body
;
record_struct_body
: struct_body
| ';'
;
Typy struktury záznamů jsou typy hodnot, jako jsou jiné typy struktur. Implicitně dědí z třídy System.ValueType
.
Modifikátory a členy struktury záznamu podléhají stejným omezením jako u struktur (přístupnost u typů, modifikátorů u členů, base(...)
inicializátory konstruktoru instance, určité přiřazení pro this
v konstruktoru, destruktory, ...). Struktury záznamů budou také dodržovat stejná pravidla jako struktury pro konstruktory instance bez parametrů a inicializátory polí, ale tento dokument předpokládá, že tato omezení pro struktury obecně zvedneme.
Viz §16.4.9 Viz konstruktory struktury bez parametrů specifikace.
Struktury záznamů nemohou používat modifikátor ref
.
Nejméně jedna částečná deklarace typu částečné struktury záznamu může poskytnout parameter_list
.
parameter_list
může být prázdný.
Parametry struktury záznamů nemůžou používat ref
, out
ani modifikátory this
(ale jsou povoleny in
a params
).
Členové struktury záznamu
Kromě členů deklarovaných v těle struktury záznamu má typ struktury záznamu další syntetizované členy. Členové jsou syntetizováni, pokud není člen s "odpovídajícím" podpisem deklarován v těle struktury záznamu nebo pokud není zděděn přístupný konkrétní nevirtuální člen s "odpovídajícím" podpisem. Dva členové se považují za shodující se, pokud mají stejný podpis nebo by se ve scénáři dědičnosti považovali za skrývající. Viz Podpisy a přetížení §7.6. Je chybou, aby člen struktury záznamu měl název "Clone".
Jedná se o chybu, že pole instance struktury záznamu má nebezpečný typ.
Struktura záznamu není povolena k deklaraci destruktoru.
Syntetizované členy jsou následující:
Členové pro rovnost
Syntetizované členy rovnosti jsou obdobné jako ve třídě záznamů (Equals
pro tento typ, Equals
pro typ object
, i ==
a !=
operátory pro tento typ).
kromě absence EqualityContract
, kontrol na null nebo dědičnosti.
Struktura záznamu implementuje System.IEquatable<R>
a zahrnuje generované silně typované přetížení Equals(R other)
, kde R
je struktura záznamu.
Metoda je public
.
Metodu lze deklarovat explicitně. Jedná se o chybu, pokud explicitní deklarace neodpovídá očekávanému podpisu nebo přístupnosti.
Pokud je Equals(R other)
uživatelsky definovaná (ne syntetizována), ale GetHashCode
není, vytvoří se upozornění.
public readonly bool Equals(R other);
Syntetizované Equals(R)
vrátí true
, pokud a jen když hodnota System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN)
pro každé datové pole fieldN
ve záznamové struktuře, kde TN
je typ pole, je true
.
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 ==
je metoda Equals(R other)
uvedená výše. Operátor !=
deleguje na operátor ==
. Jedná se o chybu, pokud jsou operátory deklarovány explicitně.
Struktura záznamu zahrnuje syntetizované překrytí 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í, které je ekvivalentní metodě deklarované následujícím způsobem:
public override readonly int GetHashCode();
Metodu lze deklarovat explicitně.
Upozornění je hlášeno, pokud jeden z Equals(R)
a GetHashCode()
je explicitně deklarován, ale druhá metoda není explicitní.
Syntetizované přepsání GetHashCode()
vrátí výsledek int
kombinování hodnot System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN)
pro každé pole instance fieldN
, přičemž TN
je typem fieldN
.
Představte si například následující strukturu záznamu:
record struct R1(T1 P1, T2 P2);
Pro tuto strukturu záznamu by syntetizované členy pro kontrolu rovnosti vypadaly přibližně 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 Combine(
EqualityComparer<T1>.Default.GetHashCode(P1),
EqualityComparer<T2>.Default.GetHashCode(P2));
}
}
Tisk členů: Metody PrintMembers a ToString
Struktura záznamu zahrnuje syntetizovanou metodu ekvivalentní metodě deklarované následujícím způsobem:
private bool PrintMembers(System.Text.StringBuilder builder);
Metoda provede následující:
- pro každý z tištěných členů záznamové struktury (nestatické veřejné pole a čitelné vlastnosti), připojí název člena, který následuje " = " a hodnotu člena oddělenou ", ".
- vrátí hodnotu true, pokud struktura záznamu má vytisknoutelné členy.
Pro člena, který má typ hodnoty, převedeme jeho hodnotu na řetězcovou reprezentaci pomocí nejúčinnější metody dostupné pro cílovou platformu. V současné době to znamená volání ToString
před předáním do StringBuilder.Append
.
Pokud tisknutelné členy záznamu neobsahují čitelný vlastnost s jiným nežreadonly
get
příslušenstvím, syntetizovaný PrintMembers
je readonly
. Není nutné, aby pole záznamu byla na readonly
, aby metodu readonly
bylo možné použít pro metodu PrintMembers
.
Metodu PrintMembers
lze deklarovat explicitně.
Jedná se o chybu, pokud explicitní deklarace neodpovídá očekávanému podpisu nebo přístupnosti.
Struktura záznamu zahrnuje syntetizovanou metodu ekvivalentní metodě deklarované následujícím způsobem:
public override string ToString();
Pokud je metoda PrintMembers
struktury záznamu readonly
, syntetizovaná ToString()
metoda je readonly
.
Metodu lze deklarovat explicitně. Jedná se o chybu, pokud explicitní deklarace neodpovídá očekávanému podpisu nebo přístupnosti.
Syntetizovaná metoda:
- vytvoří instanci
StringBuilder
, - připojí název struktury záznamu k builderu následováním " { "
- vyvolá metodu
PrintMembers
struktury záznamu s tvůrcem jako argumentem, následovaná mezerou, pokud vrátí hodnotu true. - připojí "}",
- vrátí obsah sestavovatele s
builder.ToString()
.
Představte si například 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();
}
}
Členové poziční struktury záznamu
Kromě výše uvedených členů syntetizují struktury záznamů se seznamem parametrů ("poziční záznamy") další členy se stejnými podmínkami jako výše uvedené členy.
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. Je chybou mít primární konstruktor a konstruktor s totožným podpisem již ve struktuře. Pokud deklarace typu neobsahuje seznam parametrů, není generován žádný primární konstruktor.
record struct R1
{
public R1() { } // ok
}
record struct R2()
{
public R2() { } // error: 'R2' already defines constructor with 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 objevují v těle záznamově-strukturálního těla.
Pokud má struktura záznamu primární konstruktor, jakýkoli uživatelem definovaný konstruktor musí mít explicitní inicializátor this
, 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í. Členové instance by na těchto místech způsobili chybu (podobně jako dnes členové instance v běžných inicializátorech konstruktoru jsou sice ve spojení, ale jejich použití způsobí chybu), avšak parametry primárního konstruktoru by byly v dosahu, použitelné a překryly by členy instance. 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) // definite assignment error in primary constructor
{
private int x;
public int X { get { return x; } set { x = value; } } = X;
}
Vlastnosti
Pro každý parametr záznamové struktury v deklaraci záznamové struktury 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é automatické vlastnosti
get
ainit
se vytvoří, pokud má struktura záznamu modifikátorreadonly
; v opačném případěget
aset
. Oba druhy přístupových metod sad (set
ainit
) se považují za "shodné". Uživatel tedy může deklarovat pouze inicializovatelnou vlastnost místo syntetizované měnitelné vlastnosti. Zděděná vlastnostabstract
s odpovídajícím typem se přepíše. 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á přístuporypublic
get
aset
/init
. 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 lze použít na syntetizované automatické vlastnosti a její zálohovací pole pomocí cílůproperty:
nebofield:
, které se vztahují na atributy syntakticky aplikované na odpovídající parametr struktury záznamu.
Dekonstrukce
Struktura pozičního záznamu s alespoň jedním parametrem syntetizuje veřejnou metodu instance void-returning, která se nazývá Deconstruct
s deklarací výstupního parametru pro každý parametr deklarace primárního konstruktoru. Každý parametr Deconstruct metody má stejný typ jako odpovídající parametr deklarace primárního konstruktoru. Tělo metody přiřadí každý parametr metody Deconstruct k hodnotě z přístupu k členu instance se stejným názvem.
Pokud členové instance přistupované v těle neobsahují vlastnost s jiným nežreadonly
get
příslušenstvím, syntetizovaná metoda Deconstruct
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á.
Povolit výraz with
pro struktury
Nyní je pro příjemce ve výrazu with
platné mít typ struktury.
Na pravé straně výrazu with
je member_initializer_list
s posloupností přiřazení k identifikátoru, což musí být přístupné pole instance nebo vlastnost typu příjemce.
Pro příjemce s typem struktury se příjemce nejprve zkopíruje, pak se každý member_initializer
zpracuje stejným způsobem jako přiřazení k poli nebo vlastnosti přístupu k výsledku převodu.
Přiřazení se zpracovávají v lexikálním pořadí.
Vylepšení záznamů
Povolit record class
Stávající syntaxe pro typy záznamů umožňuje record class
se stejným významem jako record
:
record_declaration
: attributes? class_modifier* 'partial'? 'record' 'class'? identifier type_parameter_list?
parameter_list? record_base? type_parameter_constraints_clause* record_body
;
Povolit poziční členy definované uživatelem, aby byly polem
Podívejte se na https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-10-05.md#changing-the-member-type-of-a-primary-constructor-parameter
Není vytvořena žádná automatická vlastnost, pokud záznam obsahuje nebo dědí pole instance s očekávaným názvem a typem.
Povolit konstruktory bez parametrů a inicializátory členů ve strukturách
Viz konstruktory struktury bez parametrů specifikace.
Otevřené otázky
- jak rozpoznat struktury záznamů v metadatech? (Nemáme nevyřčenou metodu klonování, kterou by se využilo...)
Odpovězeno
- potvrďte, že chceme zachovat návrh PrintMembers (samostatná metoda vracející
bool
) (odpověď: ano). - Potvrďte, že nepovolíme
record ref struct
(problém s poliIEquatable<RefStruct>
a ref poli) (odpověď: ano). - potvrdit implementaci členů pro rovnost. Alternativou je, že syntetizované
bool Equals(R other)
,bool Equals(object? other)
a operátory pouze všechny delegují naValueType.Equals
. (odpověď: ano) - Potvrďte, že chceme povolit inicializátory polí, pokud existuje primární konstruktor. Chceme také povolit konstruktory struktury bez parametrů, když už na tom jsme (problém s aktivátorem byl zřejmě opraven)? (odpověď: Ano, aktualizovaná specifikace by měla být zkontrolována v LDM)
- kolik chceme říct o
Combine
metodě? (odpověď: co nejmenší) - měli bychom zakázat uživatelsky definovaný konstruktor s podpisem kopírovacího konstruktoru? (odpověď: ne, neexistuje žádná koncepce konstruktoru kopírování ve specifikaci struktury záznamu)
- potvrďte, že chceme zakázat členy s jménem "Clone". (odpověď: správná)
- Pečlivě zkontrolujte, že syntetizovaná logika
Equals
je funkčně ekvivalentní implementaci za běhu programu (např. float.NaN) (odpověď: potvrzena v LDM) - mohou být atributy cílení na pole nebo vlastnosti umístěny v seznamu pozičních parametrů? (odpověď: ano, stejná jako u třídy záznamů)
-
with
generika? (odpověď: není zahrnuto v rozsahu pro C# 10) - Měl by
GetHashCode
zahrnovat hodnotu hash samotného typu, aby se získaly různé hodnoty mezirecord struct S1;
arecord struct S2;
? (odpověď: ne)
C# feature specifications