Record-strukturer
Not
Den här artikeln är en funktionsspecifikation. Specifikationen fungerar som designdokument för funktionen. Den innehåller föreslagna specifikationsändringar, tillsammans med information som behövs under utformningen och utvecklingen av funktionen. Dessa artiklar publiceras tills de föreslagna specifikationsändringarna har slutförts och införlivats i den aktuella ECMA-specifikationen.
Det kan finnas vissa skillnader mellan funktionsspecifikationen och den slutförda implementeringen. Dessa skillnader samlas in i de relevanta anteckningarna från :s LDM (Language Design Meeting).
Du kan läsa mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.
Syntaxen för en record-struct är följande:
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
| ';'
;
Poststruktureringstyper är värdetyper, som andra structtyper. De ärver implicit från klassen System.ValueType
.
Modifierare och medlemmar i en poststruktur omfattas av samma begränsningar som strukturer (tillgänglighet på typ, modifierare på medlemmar, base(...)
instanskonstruktorinitierare, definitiv tilldelning för this
i konstruktorn, destruktorer, ...). Poststrukturer följer också samma regler som strukturer för parameterlösa instanskonstruktorer och fältinitierare, men det här dokumentet förutsätter att vi lyfter dessa begränsningar för strukturer i allmänhet.
Se §16.4.9 Se parameterlösa strukturkonstruktörer spec.
Poststrukturer kan inte använda ref
-modifikator.
Högst en partiell typdeklaration av en partiell poststruct kan ge en parameter_list
.
parameter_list
kan vara tom.
Poststruktureringsparametrar kan inte använda ref
, out
eller this
modifierare (men in
och params
tillåts).
Medlemmar i en `record`-struktur
Förutom de medlemmar som deklareras i poststruktureringstexten har en post struct-typ ytterligare syntetiserade medlemmar. Medlemmar syntetiseras om inte en medlem med en "matchande" signatur deklareras i postens struct-kropp eller en tillgänglig konkret icke-virtuell medlem med en "matchande" signatur ärvs. Två medlemmar anses matcha om de har samma signatur eller skulle betraktas som "gömmer sig" i ett arvsscenario. Se Signaturer och överlagring §7.6. Det är ett fel att en medlem i en post struct får namnet "Clone".
Det är ett fel att ett instansfält i en post struct har en osäker typ.
En recordstruktur får inte deklarera en destruktor.
De syntetiserade medlemmarna är följande:
Jämställdhetsmedlemmar
De syntetiserade likhetsmedlemmarna liknar dem i en recordklass (Equals
för den här typen, Equals
för denna typ object
, ==
och !=
operatorer för den här typen),
förutom bristen på EqualityContract
, nullkontroller eller arv.
Record-structen implementerar System.IEquatable<R>
och innehåller en syntetiserad starkt typerad överbelastning av Equals(R other)
där R
är record-structen.
Metoden är public
.
Metoden kan deklareras explicit. Det är ett fel om den explicita deklarationen inte matchar den förväntade signaturen eller tillgängligheten.
Om Equals(R other)
är användardefinierad (inte syntetiserad) men GetHashCode
inte är det genereras en varning.
public readonly bool Equals(R other);
Den syntetiserade Equals(R)
returnerar true
om och endast om för varje instansfält fieldN
i posten struct värdet för System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN)
där TN
är fälttypen är true
.
Poststrukturen inkluderar syntetiserade ==
- och !=
-operatorer som motsvarar de operatorer som deklareras enligt följande:
public static bool operator==(R r1, R r2)
=> r1.Equals(r2);
public static bool operator!=(R r1, R r2)
=> !(r1 == r2);
Den Equals
metod som anropas av operatorn ==
är den Equals(R other)
metod som anges ovan. Operatorn !=
delegerar till ==
-operatorn. Det är ett fel om operatorerna deklareras explicit.
Poststructen innehåller en syntetiserad åsidosättning som motsvarar en metod som deklareras enligt följande:
public override readonly bool Equals(object? obj);
Det är ett fel om åsidosättningen deklareras explicit.
Den syntetiserade åsidosättningen returnerar other is R temp && Equals(temp)
där R
är recordstrukturen.
Poststructen innehåller en syntetiserad åsidosättning som motsvarar en metod som deklareras enligt följande:
public override readonly int GetHashCode();
Metoden kan deklareras uttryckligen.
En varning rapporteras om en av Equals(R)
och GetHashCode()
uttryckligen deklareras men den andra metoden inte är explicit.
Den syntetiserade åsidosättningen av GetHashCode()
returnerar ett int
-resultat genom att kombinera värdena av System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN)
för varje instansfält fieldN
med TN
som har typen fieldN
.
Tänk till exempel på den följande recordstrukturen:
record struct R1(T1 P1, T2 P2);
För den här poststrukturen skulle de syntetiserade jämförelsemedlemmarna vara något i stil med:
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));
}
}
Utskriftsmedlemmar: PrintMembers- och ToString-metoder
Poststructen innehåller en syntetiserad metod som motsvarar en metod som deklareras enligt följande:
private bool PrintMembers(System.Text.StringBuilder builder);
Metoden gör följande:
- för var och en av post structens utskrivbara medlemmar (icke-statiskt offentligt fält och läsbara egenskapsmedlemmar), lägger till medlemmens namn följt av " = " följt av medlemmens värde avgränsat med ", ",
- returnera sant om post structen har utskrivbara medlemmar.
För en medlem som har en värdetyp konverterar vi dess värde till en strängrepresentation med hjälp av den mest effektiva metoden som är tillgänglig för målplattformen. För närvarande innebär det att anropa ToString
innan du skickar till StringBuilder.Append
.
Om de utskrivbara medlemmarna i posten inte har en läsbar egenskap med en icke-readonly
get
-accessor, så är den syntetiserade PrintMembers
readonly
. Det finns inget krav på att postens fält ska vara readonly
för att PrintMembers
metoden ska vara readonly
.
Metoden PrintMembers
kan deklareras explicit.
Det är ett fel om den explicita deklarationen inte matchar den förväntade signaturen eller tillgängligheten.
Poststructen innehåller en syntetiserad metod som motsvarar en metod som deklareras enligt följande:
public override string ToString();
Om recordstruktursmetoden PrintMembers
är readonly
, då är den syntetiserade ToString()
-metoden readonly
.
Metoden kan deklareras explicitt. Det är ett fel om den explicita deklarationen inte matchar den förväntade signaturen eller tillgängligheten.
Den sammansatta metoden:
- skapar en
StringBuilder
instans, - lägger till post struct-namnet till byggaren följt av " { ",
- anropar rekordstrukturens
PrintMembers
-metod och ger den byggaren, följt av " " om den returnerar sant, - lägger till "}"
- returnerar byggverktygets innehåll med
builder.ToString()
.
Tänk till exempel på följande posterstruktur:
record struct R1(T1 P1, T2 P2);
För den här poststrukturen skulle de syntetiserade utskriftselementen vara ungefär:
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();
}
}
Medlemmar i positional record-structurer
Utöver ovanstående medlemmar syntetiserar poststrukturer med en parameterlista ("positionella poster") ytterligare medlemmar under samma villkor som de ovanstående medlemmarna.
Primär konstruktor
En record-struct har en offentlig konstruktor vars signatur motsvarar värdeparametrarna i typdeklarationen. Detta kallas den primära konstruktorn för typen. Det är ett fel att ha en primär konstruktor och en konstruktor med samma signatur som redan finns i struct. Om typdeklarationen inte innehåller någon parameterlista genereras ingen primär konstruktor.
record struct R1
{
public R1() { } // ok
}
record struct R2()
{
public R2() { } // error: 'R2' already defines constructor with same parameter types
}
Instansfältdeklarationer för en post struct tillåts inkludera variabelinitierare. Om det inte finns någon primär konstruktor körs instansinitierarna som en del av den parameterlösa konstruktorn. I annat fall kör den primära konstruktorn vid körning instansinitierarna som visas i post-struct-body.
Om en poststruktur har en huvudkonstruktor måste alla användardefinierade konstruktorer ha en explicit this
-konstruktorinitierare som anropar huvudkonstruktorn eller en explicit deklarerad konstruktor.
Parametrar för den primära konstruktorn samt medlemmar i record struct finns i omfånget i initierarna för instansfält eller egenskaper. Instansmedlemmar skulle utgöra ett fel i dessa sammanhang (liknande hur instansmedlemmar är i omfånget för vanliga konstruktorinitierare idag, men ett fel att använda), men parametrarna för den primära konstruktorn skulle vara i omfång och användbara och skulle skugga medlemmarna. Statiska medlemmar skulle också vara användbara.
En varning genereras om en parameter för den primära konstruktorn inte läss.
De definitiva tilldelningsreglerna för struct-instanskonstruktorer gäller för den primära konstruktorn för records. Följande är till exempel ett fel:
record struct Pos(int X) // definite assignment error in primary constructor
{
private int x;
public int X { get { return x; } set { x = value; } } = X;
}
Egenskaper
För varje record struct-parameter i en record struct-deklaration finns en motsvarande offentlig egenskap vars namn och typ tas från deklarationen av värdeparametern.
För en rekordstruktur:
- En offentlig
get
ochinit
automatisk egenskap skapas om record-strukturen harreadonly
modifierare,get
ochset
annars. Båda typerna av uppsättningsåtkomster (set
ochinit
) betraktas som "matchande". Användaren kan därför deklarera en init-only-egenskap i stället för en syntetiserad föränderlig egenskap. En ärvd egenskapabstract
med matchande typ åsidosätts. Ingen automatisk egenskap skapas om record-strukturen har ett instansfält med förväntat namn och typ. Det är ett fel om den ärvda egenskapen inte harpublic
get
ochset
/init
som åtkomstmetoder. Det är ett fel om den ärvda egenskapen eller fältet är dolt.
Den automatiska egenskapen initieras till värdet för motsvarande primära konstruktorparameter. Attribut kan tillämpas på den syntetiserade autoegenskapen och dess underliggande fält genom att användaproperty:
ellerfield:
som mål för attribut som syntaktiskt tillämpas på motsvarande poststrukturparameter.
Dekonstruera
En positionell poststruct med minst en parameter syntetiserar en offentlig void-returning-instansmetod som kallas Deconstruct
med en utparameterdeklaration för varje parameter i den primära konstruktordeklarationen. Varje parameter i metoden Deconstruct har samma typ som motsvarande parameter i den primära konstruktordeklarationen. Metodens brödtext tilldelar varje parameter i metoden Deconstruct värdet från en instansmedlemsåtkomst till en medlem med samma namn.
Om de instansmedlemmar som används i brödtexten inte innehåller någon egenskap med en icke-readonly
get
-accessor, så är den syntetiserade Deconstruct
-metoden readonly
.
Metoden kan deklareras explicit. Det är ett fel om den explicita deklarationen inte matchar den förväntade signaturen eller tillgängligheten, eller om den är statisk.
Tillåt with
-uttryck i strukturer
Det är nu giltigt för mottagaren i ett with
-uttryck att ha en strukturtyp.
Till höger i with
-uttrycket finns en member_initializer_list
med en sekvens av tilldelningar till identifierare, som måste vara ett tillgängligt instansfält eller en egenskap av mottagartypen.
För en mottagare med structtyp kopieras mottagaren först och sedan bearbetas varje member_initializer
på samma sätt som när man tilldelar ett fält eller åtkomst till en egenskap av resultatet från konverteringen.
Tilldelningar bearbetas i lexikal ordning.
Förbättringar av poster
Tillåt record class
Den befintliga syntaxen för rekordtyper tillåter record class
med samma betydelse som record
:
record_declaration
: attributes? class_modifier* 'partial'? 'record' 'class'? identifier type_parameter_list?
parameter_list? record_base? type_parameter_constraints_clause* record_body
;
Tillåt att användardefinierade positionsmedlemmar är fält
Ingen automatisk egenskap skapas om posten har eller ärver ett instansfält med förväntat namn och typ.
Tillåt parameterlösa konstruktorer och medlemsinitierare i structs
Se parameterlösa struct-konstruktorer specifikation.
Öppna frågor
- hur man känner igen poststrukturer i metadata? (Vi har ingen outsäglig klonmetod att utnyttja...)
Svarade
- bekräfta att vi vill behålla PrintMembers-designen (separat metod som returnerar
bool
) (svar: ja) - bekräfta att vi inte tillåter
record ref struct
(problem medIEquatable<RefStruct>
och referensfält) (svar: ja) - bekräfta genomförandet av jämlikhetskomponenter. Ett alternativ är att de syntetiserade
bool Equals(R other)
,bool Equals(object? other)
och operatorerna alla bara delegerar tillValueType.Equals
. (svar: ja) - bekräfta att vi vill tillåta fältinitierare när det finns en primär konstruktor. Vill vi också tillåta parameterlösa struct-konstruktorer när vi håller på (Problemet med aktiveringsaktivatorn har tydligen åtgärdats)? (svar: Ja, uppdaterad specifikation bör granskas i LDM)
- hur mycket vill vi säga om
Combine
metod? (svar: så lite som möjligt) - ska vi inte tillåta en användardefinierad konstruktor med en kopieringskonstruktorsignatur? (svar: nej, det finns ingen uppfattning om kopieringskonstruktor i post structs spec)
- bekräfta att vi inte vill tillåta medlemmar med namnet "Clone". (svar: rätt)
- dubbelkolla att syntetiserade
Equals
logik är funktionellt likvärdigt med körningsimplementering (t.ex. flyttal. NaN) (svar: bekräftat i LDM) - kan fält- eller egenskapsinriktningsattribut placeras i listan över positionsparametrar? (svar: ja, samma som för registerklass)
-
with
på generiska läkemedel? (svar: utanför omfånget för C# 10) - ska
GetHashCode
innehålla en hash av själva typen för att få olika värden mellanrecord struct S1;
ochrecord struct S2;
? (svar: nej)
C# feature specifications