Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Jegyzet
Ez a cikk egy funkcióspecifikáció. A specifikáció a funkció tervezési dokumentumaként szolgál. Tartalmazza a specifikáció javasolt módosításait, valamint a funkció tervezése és fejlesztése során szükséges információkat. Ezeket a cikkeket mindaddig közzéteszik, amíg a javasolt specifikációmódosításokat nem véglegesítik, és be nem építik a jelenlegi ECMA-specifikációba.
A szolgáltatás specifikációja és a befejezett implementáció között eltérések lehetnek. Ezeket a különbségeket a vonatkozó nyelvi tervezési értekezlet (LDM) megjegyzései rögzítik.
A funkcióspektusok C# nyelvi szabványba való bevezetésének folyamatáról a specifikációkcímű cikkben olvashat bővebben.
Bajnoki probléma: https://github.com/dotnet/csharplang/issues/39
Ez a javaslat nyomon követi a C# 9 rekordok funkció specifikációját, a C# nyelvtervező csapatának egyetértésével.
Egy rekord szintaxisa a következő:
record_declaration
: attributes? class_modifier* 'partial'? 'record' identifier type_parameter_list?
parameter_list? record_base? type_parameter_constraints_clause* record_body
;
record_base
: ':' class_type argument_list?
| ':' interface_type_list
| ':' class_type argument_list? ',' interface_type_list
;
record_body
: '{' class_member_declaration* '}' ';'?
| ';'
;
A rekordtípusok az osztálydeklarációhoz hasonló referenciatípusok. Hiba, ha egy rekord record_base
argument_list
ad meg, ha a record_declaration
nem tartalmaz parameter_list
.
Legfeljebb egy részleges típusdeklaráció adhat meg egy részleges rekordnál parameter_list
-t.
A rekordparaméterek nem használhatnak ref
, out
vagy this
módosítókat (de in
és params
engedélyezettek).
Örökség
A rekordok nem örökölhetők az osztályoktól, kivéve, ha az osztály object
, és az osztályok nem örökölhetők a rekordokból. A rekordok más rekordoktól örökölhetnek.
Rekordtípus tagjai
A rekordtörzsben deklarált tagokon kívül egy rekordtípus további szintetizált tagokkal is rendelkezik. A tagok szintetizálása csak akkor történik, ha a rekord törzsében "egyező" aláírással rendelkező tag van deklarálva, vagy ha egy "egyező" aláírással rendelkező, akadálymentes konkrét, nem virtuális tag öröklődik. Az egyező tag megakadályozza, hogy a fordító ezt a tagot hozza létre, nem pedig más szintetizált tagokat. Két tag akkor tekinthető egyezőnek, ha ugyanazzal az aláírással rendelkezik, vagy "elrejtésnek" minősül egy öröklési forgatókönyvben. Hiba, ha egy rekord tagját "Clone"-nak nevezik. Hiba forrása lehet, ha egy rekord példánymezője felső szintű mutató típusú. Beágyazott mutatótípus( például mutatótömb) használata engedélyezett.
A szintetizált tagok a következők:
Egyenlőségi tagok
Ha a rekord object
származik, a rekordtípus tartalmaz egy szintetizált, olvasható tulajdonságot, amely a következőképpen deklarált tulajdonságnak felel meg:
Type EqualityContract { get; }
A tulajdonság private
, ha a rekordtípus sealed
. Ellenkező esetben a tulajdonság virtual
és protected
.
A tulajdonság explicit módon deklarálható. Hiba, ha az explicit deklaráció nem felel meg a várt szignatúrának vagy hozzáférhetőségnek, vagy ha az explicit deklaráció nem teszi lehetővé, hogy azt egy származtatott típus felülírja, és a rekordtípus nem sealed
.
Ha a rekordtípus egy alaprekordtípusból származik, Base
, a rekordtípus tartalmaz egy szintetizált írásvédett tulajdonságot, amely a következőképpen deklarált tulajdonságnak felel meg:
protected override Type EqualityContract { get; }
A tulajdonság explicit módon deklarálható. Hiba, ha az explicit deklaráció nem felel meg a várt szignatúrának vagy hozzáférhetőségnek, vagy ha az explicit deklaráció nem teszi lehetővé, hogy azt egy származtatott típus felülírja, és a rekordtípus nem sealed
. Hiba, ha a szintetizált vagy explicit módon deklarált tulajdonság nem felülbírál egy ilyen aláírással rendelkező tulajdonságot az Base
rekordtípusban (például ha a tulajdonság hiányzik a Base
, vagy lezárt vagy nem virtuális stb.).
A szintetizált tulajdonság visszaadja typeof(R)
, ahol R
a rekordtípus.
A rekordtípus implementálja a System.IEquatable<R>
-t, és magában foglal egy szintetikus, erősen típusos túlterhelést a Equals(R? other)
számára, ahol R
a rekordtípus.
A metódus public
, és a metódus virtual
, kivéve, ha a rekordtípus sealed
.
A metódus explicit módon deklarálható. Hiba, ha az explicit deklaráció nem egyezik a várt aláírással vagy hozzáférhetőséggel, vagy az explicit deklaráció nem teszi lehetővé annak felülírását egy származtatott típusban, és a rekordtípus nem sealed
.
Ha Equals(R? other)
felhasználó által definiált (nem szintetizált), de GetHashCode
nem, figyelmeztetés jön létre.
public virtual bool Equals(R? other);
A szintetizált Equals(R?)
akkor és csak akkor ad vissza true
, ha az alábbiak mindegyike true
:
-
other
nemnull
, és - A nem örökölt rekordtípus minden példánymezője esetében
fieldN
, melyeknél a mezőtípusSystem.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN)
, azTN
értéket kap. - Ha van alaprekordtípus, akkor a
base.Equals(other)
értéke (nem virtuális hívás apublic virtual bool Equals(Base? other)
-re); egyébként aEqualityContract == other.EqualityContract
értéke.
A rekordtípus a következő módon deklarált operátorokkal egyenértékű szintetizált ==
és !=
operátorokat tartalmazza:
public static bool operator==(R? left, R? right)
=> (object)left == right || (left?.Equals(right) ?? false);
public static bool operator!=(R? left, R? right)
=> !(left == right);
A Equals
operátor által hívott ==
metódus a fent megadott Equals(R? other)
metódus. A !=
operátor a ==
operátorhoz delegál. Hiba, ha az operátorok explicit módon vannak deklarálva.
Ha a rekordtípus egy Base
alaprekordtípusból származik, a rekordtípus a következő módon deklarált metódussal egyenértékű szintetizált felülbírálást tartalmaz:
public sealed override bool Equals(Base? other);
Hiba, ha a felülbírálás explicit módon van deklarálva. Hiba történik, ha a metódus nem bírál felül egy azonos aláírással rendelkező metódust a rekordtípusú Base
-ban (például, ha a metódus hiányzik a Base
-ből, le van zárva, vagy nem virtuális, stb.).
A szintetizált felülbírálás visszaadja a Equals((object?)other)
értéket.
A rekordtípus a következőképpen deklarált metódussal egyenértékű szintetizált felülbírálást tartalmaz:
public override bool Equals(object? obj);
Hiba, ha a felülbírálás explicit módon van deklarálva. Ez hiba, ha az eljárás nem írja felül a object.Equals(object? obj)
-t (például köztes alaptípusok esetén fellépő árnyékolás miatt, stb.).
A szintetizált felülbírálás a(z) Equals(other as R)
-t adja vissza, ahol R
a rekordtípus.
A rekordtípus a következőképpen deklarált metódussal egyenértékű szintetizált felülbírálást tartalmaz:
public override int GetHashCode();
A metódus explicit módon deklarálható.
Ez akkor jelent hibát, ha az explicit deklaráció nem teszi lehetővé annak felülírását egy származtatott típusban, és a rekordtípus nem sealed
. Hiba, ha a szintetizált vagy explicit módon deklarált metódus nem bírálja felül a object.GetHashCode()
(például a köztes alaptípusok árnyékolása miatt stb.).
A rendszer figyelmeztetést jelent, ha az Equals(R?)
vagy a GetHashCode()
egyike explicit módon van deklarálva, de a másik nem explicit.
A GetHashCode()
szintetizált felülbírálása a következő értékek kombinálásának int
eredményét adja vissza:
- A nem örökölt rekordtípus minden példánymezője esetében
fieldN
, melyeknél a mezőtípusSystem.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN)
, azTN
értéket kap. - Alaprekordtípus esetén a
base.GetHashCode()
értéke; egyéb esetben aSystem.Collections.Generic.EqualityComparer<System.Type>.Default.GetHashCode(EqualityContract)
értéke.
Vegyük például a következő rekordtípusokat:
record R1(T1 P1);
record R2(T1 P1, T2 P2) : R1(P1);
record R3(T1 P1, T2 P2, T3 P3) : R2(P1, P2);
Ezekben a rekordtípusokban a szintetizált egyenlőségi tagok a következőhöz hasonlóak lehetnek:
class R1 : IEquatable<R1>
{
public T1 P1 { get; init; }
protected virtual Type EqualityContract => typeof(R1);
public override bool Equals(object? obj) => Equals(obj as R1);
public virtual bool Equals(R1? other)
{
return !(other is null) &&
EqualityContract == other.EqualityContract &&
EqualityComparer<T1>.Default.Equals(P1, other.P1);
}
public static bool operator==(R1? left, R1? right)
=> (object)left == right || (left?.Equals(right) ?? false);
public static bool operator!=(R1? left, R1? right)
=> !(left == right);
public override int GetHashCode()
{
return HashCode.Combine(EqualityComparer<Type>.Default.GetHashCode(EqualityContract),
EqualityComparer<T1>.Default.GetHashCode(P1));
}
}
class R2 : R1, IEquatable<R2>
{
public T2 P2 { get; init; }
protected override Type EqualityContract => typeof(R2);
public override bool Equals(object? obj) => Equals(obj as R2);
public sealed override bool Equals(R1? other) => Equals((object?)other);
public virtual bool Equals(R2? other)
{
return base.Equals((R1?)other) &&
EqualityComparer<T2>.Default.Equals(P2, other.P2);
}
public static bool operator==(R2? left, R2? right)
=> (object)left == right || (left?.Equals(right) ?? false);
public static bool operator!=(R2? left, R2? right)
=> !(left == right);
public override int GetHashCode()
{
return HashCode.Combine(base.GetHashCode(),
EqualityComparer<T2>.Default.GetHashCode(P2));
}
}
class R3 : R2, IEquatable<R3>
{
public T3 P3 { get; init; }
protected override Type EqualityContract => typeof(R3);
public override bool Equals(object? obj) => Equals(obj as R3);
public sealed override bool Equals(R2? other) => Equals((object?)other);
public virtual bool Equals(R3? other)
{
return base.Equals((R2?)other) &&
EqualityComparer<T3>.Default.Equals(P3, other.P3);
}
public static bool operator==(R3? left, R3? right)
=> (object)left == right || (left?.Equals(right) ?? false);
public static bool operator!=(R3? left, R3? right)
=> !(left == right);
public override int GetHashCode()
{
return HashCode.Combine(base.GetHashCode(),
EqualityComparer<T3>.Default.GetHashCode(P3));
}
}
Tagok másolása és klónozása
A rekordtípus két másolási tagot tartalmaz:
- A rekordtípus egyetlen argumentumát használó konstruktor. Ezt nevezik "másolási konstruktornak".
- Nyilvános, paraméter nélküli példányosított "clone" metódus fordító által lefoglalt névvel
A másolási konstruktor célja az állapot másolása a paraméterből az újonnan létrehozott példányba. Ez a konstruktor nem futtatja a rekorddeklarációban található példánymező-/tulajdonság-inicializálókat. Ha a konstruktor nincs explicit módon deklarálva, a fordító szintetizálja a konstruktort. Ha a rekord le van zárva, a konstruktor privát lesz, ellenkező esetben védett lesz. A kifejezetten deklarált példánykonstruktornak nyilvánosnak vagy védettnek kell lennie, kivéve, ha a rekordot lezárták. Az első dolog, amit a konstruktornak tennie kell, hogy meghívja az alap egy példánykonstruktorát, vagy egy paraméter nélküli objektumkonstruktort, ha a rekord örökli az objektumot. Hiba jelenik meg, ha egy felhasználó által definiált példánykonstruktor implicit vagy explicit konstruktor inicializálót használ, amely nem felel meg ennek a követelménynek. Az alappéldány-konstruktor meghívása után a szintetizált példánykonstruktor a rekordtípuson belül implicit módon vagy explicit módon deklarált összes példánymező értékeit másolja át. A példánykonstruktor kizárólagos jelenléte , akár explicit, akár implicit, nem akadályozza meg az alapértelmezett példánykonstruktor automatikus hozzáadását.
Ha egy virtuális "klón" metódus található az alaprekordban, a szintetizált "klón" metódus felülbírálja azt, és a metódus visszatérési típusa az aktuális tartalomtípus. Hiba keletkezik, ha az alaprekord klónozási módszere lezárva van. Ha az alaprekordban nem szerepel virtuális "klón" metódus, a klónozási módszer visszatérési típusa a tartalmazó típus, a metódus pedig virtuális, kivéve, ha a rekord lezárt vagy absztrakt. Ha a rekord absztrakciós, akkor a szintetizált klónozási módszer is absztrakt. Ha a "klónozás" metódus nem absztrakt, akkor egy másolatkonstruktor meghívásának eredményét adja vissza.
Nyomtatási tagok: PrintMembers és ToString metódusok
Ha a rekord object
származik, a rekord egy, a következőképpen deklarált metódussal egyenértékű szintetizált metódust tartalmaz:
bool PrintMembers(System.Text.StringBuilder builder);
A módszer private
, ha a rekordtípus sealed
. Ellenkező esetben a metódus virtual
és protected
.
A módszer:
- meghívja a metódust
System.Runtime.CompilerServices.RuntimeHelpers.EnsureSufficientExecutionStack()
, ha a metódus jelen van, és a rekord nyomtatható tagokkal rendelkezik. - a rekord minden nyomtatható tagjára (nem statikus nyilvános mezőre és olvasható tulajdonságtagokra) fűzi hozzá a tag nevét, majd a " = " elemet, majd a tag értékét a következővel elválasztva: ", ",
- igaz értéket ad vissza, ha a rekord nyomtatható tagokat tartalmaz.
Az értéktípussal rendelkező tagok esetében az értékét sztring-ábrázolássá alakítjuk a célplatform számára elérhető leghatékonyabb módszer használatával. Jelenleg ez azt jelenti, hogy fel kell hívnia a ToString
-t, mielőtt átmegy a StringBuilder.Append
-re.
Ha a rekordtípus egy alaprekordból származik Base
, a rekord tartalmaz egy szintetizált felülbírálást, amely egyenértékű a következő deklarált metódussal:
protected override bool PrintMembers(StringBuilder builder);
Ha a rekordnak nincsenek nyomtatható tagjai, a metódus meghívja az alap PrintMembers
metódust egy argumentummal (annak builder
paraméterével), és visszaadja az eredményt.
Ellenkező esetben a metódus:
- meghívja az alap
PrintMembers
metódust egy argumentummal (annakbuilder
paraméterével), - ha a
PrintMembers
metódus igaz értéket ad vissza, fűzze hozzá a ", " elemet a szerkesztőhöz, - a rekord minden nyomtatható tagjára vonatkozóan hozzáfűzi a tag nevét, majd az egyenlőségjel (" = ") következik, majd a tag értéke:
this.member
(vagy értéktípusok eseténthis.member.ToString()
), az elemeket pedig ", " választja el egymástól. - igaz értéket ad vissza.
A PrintMembers
metódus explicit módon deklarálható.
Hiba, ha az explicit deklaráció nem felel meg a várt szignatúrának vagy hozzáférhetőségnek, vagy ha az explicit deklaráció nem teszi lehetővé, hogy azt egy származtatott típus felülírja, és a rekordtípus nem sealed
.
A rekord egy, a következőképpen deklarált metódusnak megfelelő szintetizált metódust tartalmaz:
public override string ToString();
A metódus explicit módon deklarálható. Hiba, ha az explicit deklaráció nem felel meg a várt szignatúrának vagy hozzáférhetőségnek, vagy ha az explicit deklaráció nem teszi lehetővé, hogy azt egy származtatott típus felülírja, és a rekordtípus nem sealed
. Hiba, ha a szintetizált vagy explicit módon deklarált metódus nem bírálja felül a object.ToString()
(például a köztes alaptípusok árnyékolása miatt stb.).
A szintetizált módszer:
- létrehoz egy
StringBuilder
példányt, - hozzáfűzi a rekord nevét az építőhöz, majd a " { ",
- meghívja a rekord
PrintMembers
függvényét, amely az építőt adja meg, majd " " következik, ha igaz értéket ad vissza, - hozzáfűzi a "}" elemet,
- visszaadja a szerkesztő tartalmát
builder.ToString()
.
Vegyük például a következő rekordtípusokat:
record R1(T1 P1);
record R2(T1 P1, T2 P2, T3 P3) : R1(P1);
Azoknál a rekordtípusoknál a szintetizált nyomtatási elemek a következőképpen néznek ki:
class R1 : IEquatable<R1>
{
public T1 P1 { get; init; }
protected virtual bool PrintMembers(StringBuilder builder)
{
builder.Append(nameof(P1));
builder.Append(" = ");
builder.Append(this.P1); // or builder.Append(this.P1.ToString()); if T1 is 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();
}
}
class R2 : R1, IEquatable<R2>
{
public T2 P2 { get; init; }
public T3 P3 { get; init; }
protected override bool PrintMembers(StringBuilder builder)
{
if (base.PrintMembers(builder))
builder.Append(", ");
builder.Append(nameof(P2));
builder.Append(" = ");
builder.Append(this.P2); // or builder.Append(this.P2); if T2 is a value type
builder.Append(", ");
builder.Append(nameof(P3));
builder.Append(" = ");
builder.Append(this.P3); // or builder.Append(this.P3); if T3 is a value type
return true;
}
public override string ToString()
{
var builder = new StringBuilder();
builder.Append(nameof(R2));
builder.Append(" { ");
if (PrintMembers(builder))
builder.Append(" ");
builder.Append("}");
return builder.ToString();
}
}
Pozíciórekordtagok
A fenti tagok mellett a paraméterlistával ("pozíciórekordok") rendelkező rekordok további tagokat is szintetizálnak a fenti tagokkal azonos feltételekkel.
Elsődleges konstruktor
Egy rekordtípushoz tartozik egy nyilvános konstruktor, amelynek aláírása megfelel a típusdeklaráció értékparamétereinek. Ezt a típus elsődleges konstruktorának nevezzük, és ha van ilyen, az implicit módon deklarált alapértelmezett osztálykonstruktor el lesz tiltva. Hiba, ha egy elsődleges konstruktor és egy ugyanolyan aláírású konstruktor már megtalálható az osztályban.
Végrehajtáskor az elsődleges konstruktor
végrehajtja az osztály törzsében megjelenő példány-inicializálókat
meghívja az alaposztály-konstruktort a
record_base
záradékban megadott argumentumokkal, ha vannak ilyenek
Ha egy rekord elsődleges konstruktorsal rendelkezik, a felhasználó által definiált konstruktoroknak a "másolási konstruktor" kivételével explicit this
konstruktor inicializálóval kell rendelkezniük.
Az elsődleges konstruktor paraméterei és a rekord tagjai hatóköre a argument_list
záradék record_base
, valamint a példánymezők vagy tulajdonságok inicializálóiban található. A példánytagok ezeken a helyeken hibának minősülnének (hasonlóan ahhoz, ahogyan a példánytagok jelenleg az általános konstruktor inicializálók hatókörében szerepelnek, de hibának számít a használatuk), de az elsődleges konstruktor paraméterei hatókörben és használhatók lennének, és felülírnák a tagokat. A statikus tagok is használhatók lennének, hasonlóan ahhoz, ahogyan az alaphívások és inicializálók működnek a hagyományos konstruktorokban.
Figyelmeztetés jön létre, ha az elsődleges konstruktor egyik paramétere nem olvasható.
A argument_list
deklarált kifejezésváltozók a argument_list
hatókörében vannak. Ugyanazok az árnyékolási szabályok érvényesek, mint egy normál konstruktor inicializáló argumentumlistájában.
Tulajdonságok
A rekordtípus-deklaráció minden rekordparaméteréhez tartozik egy megfelelő nyilvánostulajdon-tag, akinek a neve és típusa az értékparaméter-deklarációból származik.
Egy rekord érdekében:
- Létrejön egy nyilvános
get
ésinit
automatikus tulajdonság (lásd különinit
tartozék specifikációját). Egy egyező típusú öröklöttabstract
tulajdonságot felülbírálják. Hiba, ha az örökölt tulajdonságpublic
felülírhatóget
ésinit
kiegészítőkkel nem rendelkezik. Hiba, ha az örökölt tulajdonság rejtett.
Az automatikus tulajdonság inicializálása a megfelelő elsődleges konstruktorparaméter értékére történik. Az attribútumok a szintetizált automatikus tulajdonságra és annak háttérmezőjére alkalmazhatók a megfelelő rekordparaméterre szintaktikailag alkalmazott attribútumokproperty:
vagyfield:
céljainak használatával.
Dekonstruktúra
Egy legalább egy paraméterrel rendelkező pozíciórekord szintetizálja a dekonstruálás nevű nyilvános érvénytelenítő példánymetódust az elsődleges konstruktor-deklaráció minden paraméteréhez tartozó kimenő paraméterdeklarációval. A Deconstruct
metódus minden paramétere ugyanazzal a típussal rendelkezik, mint az elsődleges konstruktor-deklaráció megfelelő paramétere. A metódus törzse a Deconstruct
metódus minden paraméteréhez hozzárendeli az azonos nevű példánytulajdonság értékét.
A metódus explicit módon deklarálható. Hiba, ha az explicit deklaráció nem felel meg a várt aláírásnak vagy akadálymentességnek, vagy statikus.
Az alábbi példa egy R
pozíciórekordot mutat be a fordító által szintetizált Deconstruct
metódussal, és annak használatát:
public record R(int P1, string P2 = "xyz")
{
public void Deconstruct(out int P1, out string P2)
{
P1 = this.P1;
P2 = this.P2;
}
}
class Program
{
static void Main()
{
R r = new R(12);
(int p1, string p2) = r;
Console.WriteLine($"p1: {p1}, p2: {p2}");
}
}
with
kifejezés
A with
kifejezés az alábbi szintaxist használó új kifejezés.
with_expression
: switch_expression
| switch_expression 'with' '{' member_initializer_list? '}'
;
member_initializer_list
: member_initializer (',' member_initializer)*
;
member_initializer
: identifier '=' expression
;
A with
kifejezés nem engedélyezett utasításként.
A with
kifejezés lehetővé teszi a "nem destruktív mutációt", amely a fogadó kifejezés másolatának előállítására szolgál a member_initializer_list
hozzárendeléseinek módosításával.
Egy érvényes with
kifejezés fogadója nem üres típusú. A fogadó típusának rekordnak kell lennie.
A with
kifejezés jobb oldalán található egy member_initializer_list
, amely azonosítóhozzárendeléseinek sorozatával rendelkezik, amelynek a fogadó típusának elérhető példánymezőjének vagy tulajdonságának kell lennie.
Először a rendszer meghívja a fogadó "klónozási" metódusát (fent megadott), és az eredmény a fogadó típusára lesz konvertálva. Ezután minden member_initializer
ugyanúgy kerül feldolgozásra, mint az átalakítás eredményéhez tartozó mező vagy tulajdonság elérésére történő hozzárendelés. A hozzárendelések lexikális sorrendben vannak feldolgozva.
C# feature specifications