Datensätze
Hinweis
Dieser Artikel ist eine Feature-Spezifikation. Die Spezifikation dient als Designdokument für das Feature. Es enthält vorgeschlagene Spezifikationsänderungen sowie Informationen, die während des Entwurfs und der Entwicklung des Features erforderlich sind. Diese Artikel werden veröffentlicht, bis die vorgeschlagenen Spezifikationsänderungen abgeschlossen und in die aktuelle ECMA-Spezifikation aufgenommen werden.
Es kann einige Abweichungen zwischen der Feature-Spezifikation und der abgeschlossenen Implementierung geben. Diese Unterschiede werden in den entsprechenden Hinweisen zum Language Design Meeting (LDM) erfasst.
Weitere Informationen zum Prozess für die Aufnahme von Funktions-Speclets in den C#-Sprachstandard finden Sie im Artikel zu den Spezifikationen.
Dieser Vorschlag verfolgt die Spezifikation für das C# 9-Datensatzmerkmal, wie es vom C#-Sprachdesignteam vereinbart wurde.
Die Syntax für einen Datensatz lautet wie folgt:
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* '}' ';'?
| ';'
;
Datensatztypen sind Referenztypen, ähnlich einer Klassendeklaration. Es ist ein Fehler, wenn ein Datensatz eine record_base
argument_list
bereitstellt, wenn die record_declaration
keine parameter_list
enthält.
Höchstens eine partielle Typdeklaration eines partiellen Datensatzes darf ein parameter_list
enthalten.
Datensatz-Parameter können keine ref
, out
oder this
Modifikatoren verwenden (in
und params
sind jedoch zulässig).
Vererbung
Datensätze können nicht von Klassen erben, es sei denn, die Klasse ist object
, und Klassen können nicht von Datensätzen erben. Datensätze können von anderen Datensätzen erben.
Mitglieder eines Datensatztyps
Zusätzlich zu den im Datensatzkörper deklarierten Mitgliedern verfügt ein Datensatztyp über zusätzliche synthetisierte Mitglieder. Mitglieder werden synthetisiert, es sei denn, ein Mitglied mit einer „übereinstimmenden” Signatur wird im Body des Datensatzes deklariert oder ein zugreifbares konkretes nicht-virtuelles Mitglied mit einer „übereinstimmenden” Signatur wird vererbt. Ein übereinstimmendes Mitglied verhindert, dass der Compiler dieses Mitglied generiert, nicht andere synthetisierte Mitglieder. Zwei Mitglieder werden als übereinstimmend betrachtet, wenn sie die gleiche Signatur haben oder in einem Vererbungsszenario als "versteckt" betrachtet werden würden. Es ist ein Fehler, wenn ein Mitglied eines Datensatzes „Clone” genannt wird. Es ist ein Fehler, wenn ein Instanzfeld eines Datensatzes einen Zeigertyp auf oberster Ebene aufweist. Ein geschachtelter Zeigertyp, z. B. ein Array von Zeigern, ist zulässig.
Die synthetisierten Mitglieder sind wie folgt:
Equality-Mitglieder
Wenn der Datensatz von object
abgeleitet wird, enthält der Datensatztyp eine synthetisierte schreibgeschützte Eigenschaft, die einer wie folgt deklarierten Eigenschaft entspricht:
Type EqualityContract { get; }
Die Eigenschaft lautet private
, wenn der Datensatztyp sealed
ist. Andernfalls lautet die Eigenschaft virtual
und protected
.
Die Eigenschaft kann explizit deklariert werden. Es ist ein Fehler, wenn die explizite Deklaration nicht mit der erwarteten Signatur oder Zugänglichkeit übereinstimmt, oder wenn die explizite Deklaration das Überschreiben in einem abgeleiteten Typ nicht zulässt und der Datensatztyp nicht sealed
ist.
Wenn der Datensatztyp von einem Basiseintragstyp Base
abgeleitet wird, enthält der Datensatztyp eine synthetisierte schreibgeschützte Eigenschaft, die einer wie folgt deklarierten Eigenschaft entspricht:
protected override Type EqualityContract { get; }
Die Eigenschaft kann explizit deklariert werden. Es ist ein Fehler, wenn die explizite Deklaration nicht mit der erwarteten Signatur oder Zugänglichkeit übereinstimmt, oder wenn die explizite Deklaration das Überschreiben in einem abgeleiteten Typ nicht zulässt und der Datensatztyp nicht sealed
ist. Es ist ein Fehler, wenn eine synthetisierte oder explizit deklarierte Eigenschaft keine Eigenschaft mit dieser Signatur im Datensatztyp Base
überschreibt (z. B. wenn die Eigenschaft im Base
fehlt, oder versiegelt oder nicht virtuell usw.).
Die synthetisierte Eigenschaft gibt typeof(R)
zurück, wobei R
der Datensatztyp ist.
Der Datensatztyp implementiert System.IEquatable<R>
und enthält eine synthetisierte, stark typisierte Überladung von Equals(R? other)
, wobei R
der Datensatztyp ist.
Die Methode ist public
, und die Methode ist virtual
, es sei denn, der Datensatztyp ist sealed
.
Die Methode kann explizit deklariert werden. Es ist ein Fehler, wenn die explizite Deklaration nicht mit der erwarteten Signatur oder Zugänglichkeit übereinstimmt, oder die explizite Deklaration das Überschreiben in einem abgeleiteten Typ nicht zulässt und der Datensatztyp nicht sealed
ist.
Wenn Equals(R? other)
benutzerdefiniert (nicht synthetisiert) ist, aber GetHashCode
nicht benutzerdefiniert ist, wird eine Warnung erzeugt.
public virtual bool Equals(R? other);
Der synthetisierte Equals(R?)
gibt true
zurück, genau dann, wenn jeder der folgenden Einträge true
ist:
-
other
ist ungleichnull
und - Für jedes Instanzfeld
fieldN
in einem Datensatztyp, der nicht vererbt wird, gilt der Wert vonSystem.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN)
, wobeiTN
der Feldtyp ist, und - Wenn ein Basisdatensatztyp vorhanden ist, wird der Wert von
base.Equals(other)
(ein nicht virtueller Aufruf anpublic virtual bool Equals(Base? other)
) verwendet; andernfalls der Wert vonEqualityContract == other.EqualityContract
.
Der Datensatztyp enthält synthetisierte ==
- und !=
-Operatoren, die den wie folgt deklarierten Operatoren entsprechen:
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);
Die Equals
-Methode, die vom ==
-Operator aufgerufen wird, ist die oben angegebene Equals(R? other)
-Methode. Der !=
-Operator delegiert an den ==
-Operator. Es ist ein Fehler, wenn die Operatoren explizit deklariert werden.
Wenn der Datensatztyp von einem Basiseintragstyp Base
abgeleitet wird, enthält der Datensatztyp eine synthetisierte Überschreibung, die einer wie folgt deklarierten Methode entspricht:
public sealed override bool Equals(Base? other);
Es ist ein Fehler, wenn die Überschreibung explizit deklariert wird. Es ist ein Fehler, wenn die Methode eine Methode nicht mit derselben Signatur im Datensatztyp Base
überschreibt (z. B. wenn die Methode im Base
fehlt, oder versiegelt oder nicht virtuell usw.).
Die synthetisierte Überschreibung gibt Equals((object?)other)
zurück.
Der Datensatztyp enthält eine synthetisierte Überschreibung, die einer wie folgt deklarierten Methode entspricht:
public override bool Equals(object? obj);
Es ist ein Fehler, wenn die Überschreibung explizit deklariert wird. Es ist ein Fehler, wenn die Methode object.Equals(object? obj)
nicht überschreibt (z. B. aufgrund von Schatten in Zwischenbasistypen usw.).
Die synthetisierte Überschreibung gibt Equals(other as R)
zurück, wobei R
der Datensatztyp ist.
Der Datensatztyp enthält eine synthetisierte Überschreibung, die einer wie folgt deklarierten Methode entspricht:
public override int GetHashCode();
Die Methode kann explizit deklariert werden.
Es ist ein Fehler, wenn die explizite Deklaration das Überschreiben in einem abgeleiteten Typ nicht zulässt und der Datensatztyp nicht sealed
ist. Es handelt sich um einen Fehler, wenn eine synthetisierte oder explizit deklarierte Methode object.GetHashCode()
nicht überschreibt (z. B. aufgrund von Schatten in Zwischenbasistypen usw.).
Eine Warnung wird geliefert, wenn eine der Methoden Equals(R?)
und GetHashCode()
explizit deklariert ist, die andere Methode jedoch nicht explizit.
Die synthetisierte Überschreibung von GetHashCode()
ergibt als int
das Ergebnis der Kombination folgender Werte:
- Für jedes Instanzfeld
fieldN
in einem Datensatztyp, der nicht vererbt wird, gilt der Wert vonSystem.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN)
, wobeiTN
der Feldtyp ist, und - Wenn ein Basisdatensatztyp vorhanden ist, der Wert
base.GetHashCode()
; andernfalls der WertSystem.Collections.Generic.EqualityComparer<System.Type>.Default.GetHashCode(EqualityContract)
.
Berücksichtigen Sie beispielsweise folgende Datensatztypen:
record R1(T1 P1);
record R2(T1 P1, T2 P2) : R1(P1);
record R3(T1 P1, T2 P2, T3 P3) : R2(P1, P2);
Bei diesen Datensatztypen würden die synthetisierten Gleichheitsmitglieder etwa wie folgt aussehen:
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));
}
}
Methoden zum Kopieren und Klonen von Membern
Ein Datensatztyp enthält zwei kopierte Mitglieder:
- Ein Konstruktor, der ein einzelnes Argument des Datensatztyps entgegennimmt. Sie wird als „Kopierkonstruktor” bezeichnet.
- Eine synthetisierte öffentliche parameterlose Instanz-„Clone”-Methode mit einem compilerreservierten Namen
Der Zweck des Kopierkonstruktors besteht darin, den Zustand vom Parameter in die neue Instanz zu kopieren, die erstellt wird. Dieser Konstruktor führt keine Instanzfeld-/Eigenschaftsinitialisierer aus, die in der Datensatzdeklaration vorhanden sind. Wenn der Konstruktor nicht explizit deklariert wird, wird ein Konstruktor vom Compiler synthetisiert. Wenn der Datensatz versiegelt ist, ist der Konstruktor privat, andernfalls wird er geschützt. Ein explizit deklarierter Kopierkonstruktor muss entweder öffentlich oder geschützt sein, es sei denn, der Datensatz ist versiegelt. Als Erstes muss der Konstruktor einen Kopierkonstruktor der Basis aufrufen oder, falls der Datensatz vom Objekt erbt, einen parameterlosen Objektkonstruktor. Wenn ein benutzerdefinierter Kopierkonstruktor einen impliziten oder expliziten Konstruktorinitialisierer verwendet, der diese Anforderung nicht erfüllt, wird ein Fehler gemeldet. Nachdem ein Basiskopierkonstruktor aufgerufen wurde, kopiert ein synthetisierter Kopierkonstruktor die Werte für alle Instanzfelder, die implizit oder explizit innerhalb des Datensatztyps deklariert sind. Das alleinige Vorhandensein eines Kopierkonstruktors, ob explizit oder implizit, verhindert nicht das automatische Hinzufügen eines Standardinstanzkonstruktors.
Wenn eine virtuelle „Clone”-Methode im Basisdatensatz vorhanden ist, überschreibt die synthetisierte „Clone”-Methode sie, und der Rückgabetyp der Methode ist der aktuelle enthaltende Typ. Wenn die „Clone”-Methode für den Basisdatensatz versiegelt ist, wird ein Fehler erzeugt. Wenn eine virtuelle „Clone”-Methode nicht im Basisdatensatz vorhanden ist, ist der Rückgabetyp der „Clone”-Methode der enthaltende Typ, und die Methode ist virtuell, es sei denn, der Datensatz ist versiegelt oder abstrakt. Wenn der enthaltende Datensatz abstrakt ist, ist die synthetisierte Klonmethode ebenfalls abstrakt. Wenn die „Clone”-Methode nicht abstrahiert ist, wird das Ergebnis eines Aufrufs eines Kopierkonstruktors zurückgegeben.
Druck-Mitglieder: PrintMembers- und ToString-Methoden
Wenn der Datensatz von object
abgeleitet wird, enthält der Datensatz eine synthetisierte Methode, die einer wie folgt deklarierten Methode entspricht:
bool PrintMembers(System.Text.StringBuilder builder);
Die Methode ist private
, wenn der Datensatztyp sealed
ist. Andernfalls wird die Methode virtual
und protected
.
Die -Methode:
- ruft die Methode
System.Runtime.CompilerServices.RuntimeHelpers.EnsureSufficientExecutionStack()
auf, wenn die Methode vorhanden ist und der Datensatz druckbare Mitglieder aufweist. - fügt für jedes Druck-Mitglied des Datensatzes (nicht statisches öffentliches Feld und lesbare Eigenschaftsmitglieder) den Namen des Mitglieds an, gefolgt von „ = ” und dem Wert des Mitglieds, getrennt durch „ , ”,
- gibt „true” zurück, wenn der Datensatz Druck-Mitglieder hat.
Für ein Mitglied mit einem Werttyp werden wir dessen Wert mithilfe der effizientesten, für die Zielplattform verfügbaren Methode in eine Zeichenfolgendarstellung konvertieren. Zur Zeit bedeutet das, dass Sie ToString
aufrufen, bevor Sie StringBuilder.Append
übergeben.
Wenn der Datensatztyp von einem Basiseintrag Base
abgeleitet wird, enthält der Datensatz eine synthetisierte Überschreibung, die einer wie folgt deklarierten Methode entspricht:
protected override bool PrintMembers(StringBuilder builder);
Wenn der Datensatz keine druckbaren Elemente aufweist, ruft die Methode die Basismethode PrintMembers
mit einem Argument auf (ihrem builder
-Parameter) und gibt das Ergebnis zurück.
Andernfalls lautet die Methode:
- ruft die Base-
PrintMembers
-Methode mit einem Argument (dessenbuilder
-Parameter) auf. - wenn die
PrintMembers
-Methode „true” zurückgegeben hat, fügen Sie „ , ” an den Generator an, - fügt für jedes druckbare Mitglied des Datensatzes den Namen dieses Mitglieds gefolgt von „ = ” gefolgt vom Wert des Mitglieds an:
this.member
(oderthis.member.ToString()
für Werttypen), getrennt mit „ , ”, - „true” zurückgeben.
Die Methode PrintMembers
kann explizit deklariert werden.
Es ist ein Fehler, wenn die explizite Deklaration nicht mit der erwarteten Signatur oder Zugänglichkeit übereinstimmt, oder wenn die explizite Deklaration das Überschreiben in einem abgeleiteten Typ nicht zulässt und der Datensatztyp nicht sealed
ist.
Der Datensatz enthält eine synthetisierte Methode, die einer wie folgt deklarierten Methode entspricht:
public override string ToString();
Die Methode kann explizit deklariert werden. Es ist ein Fehler, wenn die explizite Deklaration nicht mit der erwarteten Signatur oder Zugänglichkeit übereinstimmt, oder wenn die explizite Deklaration das Überschreiben in einem abgeleiteten Typ nicht zulässt und der Datensatztyp nicht sealed
ist. Es handelt sich um einen Fehler, wenn eine synthetisierte oder explizit deklarierte Methode object.ToString()
nicht überschreibt (z. B. aufgrund von Schatten in Zwischenbasistypen usw.).
Die synthetisierte Methode:
- erstellt eine
StringBuilder
-Instanz, - fügt den Namen des Datensatzes an den Builder an, gefolgt von „ { ”,
- ruft die
PrintMembers
-Methode des Datensatzes auf und übergibt ihm den Builder, gefolgt von „ ”, wenn sie „true” zurückgibt, - fügt "}" an,
- gibt den Inhalt des Builders mit
builder.ToString()
zurück.
Berücksichtigen Sie beispielsweise folgende Datensatztypen:
record R1(T1 P1);
record R2(T1 P1, T2 P2, T3 P3) : R1(P1);
Bei diesen Datensatztypen würden die synthetisierten Druck-Mitglieder etwa wie folgt aussehen:
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();
}
}
Mitglieder des positionsbezogenen Datensatzes
Zusätzlich zu den oben genannten Mitgliedern synthetisieren Datensätze mit einer Parameterliste („Positionsdatensätze”) weitere Mitglieder mit den gleichen Bedingungen wie die oben genannten Mitglieder.
Primärer Konstruktor
Ein Datensatztyp verfügt über einen öffentlichen Konstruktor, dessen Signatur den Wertparametern der Typdeklaration entspricht. Dies wird als primärer Konstruktor für den Typ bezeichnet und bewirkt, dass der implizit deklarierte Standardklassenkonstruktor (sofern vorhanden) unterdrückt wird. Es ist ein Fehler, wenn ein primärer Konstruktor und ein Konstruktor mit der gleichen Signatur bereits in der Klasse vorhanden sind.
Zur Laufzeit des primären Konstruktors
führt die Instanzinitialisierer aus, die im Klassenkörper angezeigt werden
ruft den Basisklassenkonstruktor mit den in der
record_base
-Klausel angegebenen Argumenten auf, sofern diese vorhanden ist
Wenn ein Datensatz über einen primären Konstruktor verfügt, muss ein benutzerdefinierter Konstruktor, außer für den Kopierkonstruktor, über einen expliziten this
-Konstruktorinitialisierer verfügen.
Parameter des Primärkonstruktors sowie die Mitglieder des Rekords befinden sich im Bereich der argument_list
der record_base
-Klausel und in Initialisierern von Instanzfeldern oder Eigenschaften. Instanzmitglieder wären an diesen Orten ein Fehler (ähnlich wie Instanzmitglieder heute in regulären Konstruktorinitialisierern im Bereich sind, aber ein Fehler bei der Verwendung), aber die Parameter des primären Konstruktors wären im Bereich und verwendbar und würden Mitglieder überschatten. Statische Mitglieder können auch verwendet werden, ähnlich wie Basisaufrufe und Initialisierer heute in normalen Konstruktoren funktionieren.
Eine Warnung wird erzeugt, wenn ein Parameter des primären Konstruktors nicht gelesen wird.
Ausdrucksvariablen, die im argument_list
deklariert sind, sind im Geltungsbereich von argument_list
. Dieselben Schattierungsregeln gelten wie in der Argumentliste eines regulären Konstruktorinitialisierers.
Eigenschaften
Für jeden Datensatzparameter einer Datensatztypdeklaration gibt es ein entsprechendes öffentliches Eigenschaftselement, dessen Name und Typ aus der Wertparameterdeklaration entnommen werden.
Zur Information:
- Eine öffentliche Auto-Eigenschaft für
get
undinit
wird definiert (siehe separate Accessor-Spezifikationinit
). Eine geerbteabstract
-Eigenschaft mit passendem Typ wird überschrieben. Es ist ein Fehler, wenn die geerbte Eigenschaft nicht überpublic
überschreibbarenget
undinit
-Accessoren verfügt. Es handelt sich um einen Fehler, wenn die geerbte Eigenschaft verborgen ist.
Die Auto-Eigenschaft wird mit dem Wert des entsprechenden primären Konstruktorparameters initialisiert. Attribute können auf die synthetisierte Auto-Eigenschaft und ihr zugehöriges Feld angewendet werden, indem Sieproperty:
- oderfield:
-Ziele für Attribute verwenden, die syntaktisch auf den entsprechenden Datensatz-Parameter angewendet werden.
Dekonstruktion
Ein Datensatz mit mindestens einem Parameter synthetisiert eine öffentliche Instanz-Methode mit Rückgabewert namens „Deconstruct” mit einer Out-Parameter-Deklaration für jeden Parameter der primären Konstruktor-Deklaration. Jeder Parameter der Deconstruct
-Methode weist den gleichen Typ wie der entsprechende Parameter der primären Konstruktordeklaration auf. Der Methodenkörper weist jedem Parameter der Deconstruct
-Methode den Wert der Instanzeigenschaft desselben Namens zu.
Die Methode kann explizit deklariert werden. Es ist ein Fehler, wenn die explizite Deklaration nicht mit der erwarteten Signatur oder Barrierefreiheit übereinstimmt oder statisch ist.
Das folgende Beispiel zeigt einen Positionsdatensatz R
mit der vom Compiler erzeugten Deconstruct
-Methode zusammen mit deren Verwendung:
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
Ausdruck
Ein with
-Ausdruck ist eine neue Ausdrucksform mit der folgenden Syntax.
with_expression
: switch_expression
| switch_expression 'with' '{' member_initializer_list? '}'
;
member_initializer_list
: member_initializer (',' member_initializer)*
;
member_initializer
: identifier '=' expression
;
Ein with
-Ausdruck ist nicht als Anweisung zulässig.
Ein with
-Ausdruck ermöglicht eine „nicht destruktive Mutation”, die entwickelt wurde, um eine Kopie des Empfängerausdrucks mit Änderungen in Zuordnungen in der member_initializer_list
zu erzeugen.
Ein gültiger with
-Ausdruck weist einen Empfänger mit einem nicht ungültigen Typ auf. Der Empfängertyp muss ein Datensatz sein.
Auf der rechten Seite des with
-Ausdrucks befindet sich ein member_initializer_list
mit einer Sequenz von Zuweisungen an identifier, der ein zugriffsfreies Instanzfeld oder eine Eigenschaft vom Typ des Empfängers sein muss.
Zuerst wird die „Clone”-Methode des Empfängers (oben angegeben) aufgerufen, und das Ergebnis wird in den Typ des Empfängers konvertiert. Anschließend wird jeder member_initializer
auf die gleiche Weise verarbeitet wie eine Zuweisung zu einem Feld oder einem Zugriff auf eine Eigenschaft des Konvertierungsergebnisses. Zuweisungen werden in lexikalischer Reihenfolge verarbeitet.
C# feature specifications