Freigeben über


Partielle Eigenschaften

Anmerkung

Dieser Artikel ist eine Featurespezifikation. 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 Featurespezifikation und der abgeschlossenen Implementierung geben. Diese Unterschiede werden in den entsprechenden Hinweisen zum Language Design Meeting (LDM) erfasst.

Weitere Informationen zum Einführen von Featurespezifikationen in den C#-Sprachstandard finden Sie im Artikel zu den Spezifikationen.

Champion-Problem: https://github.com/dotnet/csharplang/issues/6420

Grammatik

Die Grammatik von property_declaration(§14.7.1) wird wie folgt aktualisiert:

property_declaration
-    : attributes? property_modifier* type member_name property_body
+    : attributes? property_modifier* 'partial'? type member_name property_body
    ;  

Anmerkungen: Dies ist vergleichbar mit der Spezifikation von method_header(§15.6.1) und class_declaration(§15.2.1). (Beachten Sie, dass ProbIem Nr. 946 vorschlägt, die Anforderung an die Reihenfolge zu lockern, und dies wahrscheinlich für alle Deklarationen gelten würde, die den partial-Modifikator zulassen. Wir beabsichtigen, eine solche Lockerung der Reihenfolge in naher Zukunft festzulegen und sie im selben Release umzusetzen, in dem diese Funktion implementiert wird.)

Definieren und Implementieren von Deklarationen

Wenn eine Eigenschaftsdeklaration einen partiellen Modifizierer enthält, wird diese Eigenschaft als partielle Eigenschaftbezeichnet. Partielle Eigenschaften können nur als Mitglieder von Teiltypen deklariert werden.

Die Deklaration einer partiellen Eigenschaft wird als definierende Deklaration bezeichnet, wenn ihre Zugriffsmethoden alle Semikolons enthalten und der extern-Modifikator fehlt. Andernfalls handelt es sich um eine Implementierungsdeklaration .

partial class C
{
    // Defining declaration
    public partial string Prop { get; set; }

    // Implementing declaration
    public partial string Prop { get => field; set => field = value; }
}

Da wir die syntaktische Form mit Semikolons in Zugriffsmethoden für die definierende Deklaration reserviert haben, kann eine partielle Eigenschaft nicht automatisch implementiert werden. Wir passen daher automatisch implementierte Eigenschaften (§15.7.4) wie folgt an:

Eine automatisch implementierte Eigenschaft (oder kurz Auto-Eigenschaft) ist eine nicht abstrakte, nicht externe, nicht partielle, nicht referenzwertige Eigenschaft mit Nur-Semikolon-Zugriffsmethoden.

Anmerkungen. Es ist nützlich, dass der Compiler in der Lage ist, eine einzelne Deklaration isoliert zu betrachten und zu wissen, ob es sich um eine Definition oder eine Implementierungsdeklaration handelt. Daher möchten wir keine automatischen Eigenschaften zulassen, indem beispielsweise zwei identische partial Eigenschaftendeklarationen eingeschlossen werden. Wir denken nicht, dass die Anwendungsfälle für dieses Feature die Implementierung der partiellen Eigenschaft mit einer automatischen Eigenschaft umfassen, aber in Fällen, in denen eine triviale Implementierung gewünscht wird, denken wir, dass das field Schlüsselwort die Dinge einfach genug macht.


Eine partielle Eigenschaft muss eine definierende Deklaration und eine implementierende Deklarationhaben.

Anmerkungen. Wir glauben auch nicht, dass es nützlich ist, die Deklaration über mehr als zwei Teile aufzuteilen, um z. B. verschiedene Accessoren an verschiedenen Stellen zu implementieren. Deshalb imitieren wir einfach das Schema, das durch Partielle Methoden festgelegt wurde.


Nur die definierende Deklaration einer partiellen Eigenschaft nimmt an der Suche teil, ähnlich wie die definierende Deklaration einer partiellen Methode an der Überladungsauflösung beteiligt ist.

Anmerkungen. Im Compiler würden wir davon ausgehen, dass nur das Symbol für die definierende Deklaration in der Memberliste angezeigt wird, und auf das Symbol für den implementierenden Teil kann über das definierende Symbol zugegriffen werden. Einige Funktionen wie die Analyse mit Null-Wert könnten jedoch bis zur implementierenden Deklaration durchsehen, um ein nützlicheres Verhalten zu ermöglichen.

partial class C
{
    public partial string Prop { get; set; }
    public partial string Prop { get => field; set => field = value; }

    public C() // warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
    {
    }
}

Eine partielle Eigenschaft darf den abstract-Modifikator nicht haben.

Eine partielle Eigenschaft kann keine Schnittstelleneigenschaften explizit implementieren.

Attributzusammenführung

Ähnlich wie bei sogenannten partiellen Methoden, werden die Attribute in der resultierenden Eigenschaft als kombinierte Attribute der Teile in einer nicht spezifizierten Reihenfolge zusammengeführt, und Duplikate werden nicht entfernt.

Aufruferinformationsattribute

Wir passen die folgende Formulierung des Standards an:

Es ist ein Fehler, wenn ein Parameter sowohl im definierenden als auch im implementierenden Teil einer partiellen Methoden-Member-Deklaration das gleiche Aufruferinformationsattribut hat. Nur Aufrufer-Info-Attribute im definierenden Teil werden angewendet, während Aufrufer-Info-Attribute, die nur im implementierenden Teil auftreten, ignoriert werden.

  • Der beschriebene Fehler fällt außerhalb der Definitionen dieser Attribute, da sie kein AllowMultiple = truehaben. Wenn Sie sie mehrmals verwenden, einschließlich teilweiser Deklarationen, tritt ein Fehler auf.
  • Wenn Aufrufer-Info-Attribute auf einen Parameter im Implementierungsteil einer partiellen Methode angewendet werden, meldet der Roslyn-Compiler eine Warnung. Außerdem wird eine Warnung für dasselbe Szenario in einer partiellen Eigenschaft angezeigt.

Abgleichen von Signaturen

Bei der LDM-Sitzung am 14. September 2020 wurde eine Reihe von „strengen“ Anforderungen für den Signaturabgleich von partiellen Methoden definiert, die in einer Warnwelle eingeführt wurden. Für partielle Eigenschaften gelten weitestgehend analoge Anforderungen wie für partielle Methoden für den Signaturabgleich, mit der Ausnahme, dass alle Diagnosen für Abweichungen standardmäßig gemeldet werden und nicht hinter einer Warnwelle verborgen bleiben.

Zu den Anforderungen für den Signaturabgleich gehören:

  1. Typen- und Referenztypenunterschiede zwischen partiellen Eigenschaftsdeklarationen, die für die Laufzeit erheblich sind, führen zu einem Kompilierungszeitfehler.
  2. Unterschiede bei Tupelelementnamen innerhalb partieller Eigenschaftsdeklarationen führen zu einem Kompilierungsfehler, identisch mit partiellen Methoden.
  3. Die Eigenschaftendeklarationen und ihre Accessordeklarationen müssen dieselben Modifier haben, auch wenn die Modifier möglicherweise in einer anderen Reihenfolge erscheinen.
    • Ausnahme: Dies gilt nicht für den extern-Modifikator, der nur in einer implementierenden Deklaration vorkommen darf.
  4. Alle anderen syntaktischen Unterschiede in den Signaturen partieller Eigenschaftsdeklarationen führen zu einer Kompilierungszeitwarnung mit den folgenden Ausnahmen:
    • Attributlisten für oder innerhalb von partiellen Eigenschaftsdeklarationen müssen nicht übereinstimmen. Stattdessen werden Attribute an entsprechenden Positionen gemäß Attributzusammenführung zusammengeführt.
    • Unterschiede im Nullable-Kontext verursachen keine Warnungen. Mit anderen Worten: Ein Unterschied, bei dem bei einem der Typen nicht bekannt ist, ob NULL-Werte zugelassen sind und der andere Typ mit Anmerkungen für die NULL-Zulässigkeit versehen ist, führt zu keinen Warnungen.
    • Standard-Parameterwerte müssen nicht übereinstimmen. Eine Warnung wird gemeldet, wenn der Implementierungsteil eines partiellen Indexers Standardwerte hat. Dies ähnelt einer vorhandenen Warnung, die auftritt, wenn der Implementierungsteil einer partiellen Methode Standardwerte aufweist.
  5. Eine Warnung tritt auf, wenn Parameternamen sich beim Definieren und Implementieren von Deklarationen unterscheiden. Die Parameternamen aus dem Definitionsteil werden an Verwendungsstellen und in emit verwendet.
  6. Nullbarkeitsunterschiede, die keine verschleierte Nullbarkeit erfordern, führen zu Warnungen. Beim Analysieren einer Zugriffsmethode wird die Signatur des Implementierungsteils verwendet. Die Definitionsteilsignatur wird beim Analysieren von Verwendungsstellen und in Emit verwendet. Dies steht im Einklang mit partiellen Methoden.
partial class C1
{
    public partial string Prop { get; private set; }

    // Error: accessor modifier mismatch in 'set' accessor of 'Prop'
    public partial string Prop { get => field; set => field = value; }
}

partial class C2
{
    public partial string Prop { get; init; }

    // Error: implementation of 'Prop' must have an 'init' accessor to match definition
    public partial string Prop { get => field; set => field = value; }
}

partial class C3
{
    public partial string Prop { get; }

    // Error: implementation of 'Prop' cannot have a 'set' accessor because the definition does not have a 'set' accessor.
    public partial string Prop { get => field; set => field = value; }
}

partial class C4
{
    public partial string this[string s = "a"] { get; set; }
    public partial string this[string s] { get => s; set { } } // ok

    public partial string this[int i, string s = "a"] { get; set; }
    public partial string this[int i, string s = "a"] { get => s; set { } } // CS1066: The default value specified for parameter 's' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments
}

Dokumentationskommentare

Wir möchten, dass das Verhalten von Doc-Kommentaren zu partiellen Eigenschaften mit dem, was wir für Teilmethoden geliefert haben, konsistent sein soll. Dieses Verhalten ist in https://github.com/dotnet/csharplang/issues/5193detailliert beschrieben.

Es ist zulässig, Dokumentkommentare entweder zu der Definition oder zum Implementierungsteil einer partiellen Eigenschaft einzuschließen. (Beachten Sie, dass Dokumentkommentare für Eigenschaftszugriffsmethoden nicht unterstützt werden.)

Wenn Dokumentkommentare nur für einen der Teile der Eigenschaft vorhanden sind, werden diese Dokumentkommentare normalerweise verwendet (in ISymbol.GetDocumentationCommentXml()dargestellt, in die XML-Dokumentationsdatei geschrieben usw.).

Wenn Dokumentkommentare in beiden Teilen vorhanden sind, werden alle Dokumentkommentare des Definitionsteils gelöscht, und es werden nur die Dokumentkommentare für den Implementierungsteil verwendet.

Beispiel: Das folgende Programm:

/// <summary>
/// My type
/// </summary>
partial class C
{
    /// <summary>Definition part comment</summary>
    /// <returns>Return value comment</returns>
    public partial int Prop { get; set; }
    
    /// <summary>Implementation part comment</summary>
    public partial int Prop { get => 1; set { } }
}

Ergibt die folgende XML-Dokumentationsdatei:

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>ConsoleApp1</name>
    </assembly>
    <members>
        <member name="T:C">
            <summary>
            My type
            </summary>
        </member>
        <member name="P:C.Prop">
            <summary>
            Implementation part comment
            </summary>
        </member>
    </members>
</doc>

Wenn Parameternamen zwischen Teildeklarationen unterschiedlich sind, verwenden <paramref> Elemente die Parameternamen aus der Deklaration, die dem Dokumentationskommentar im Quellcode zugeordnet ist. Beispielsweise bezieht sich ein Paramref für einen Dokumentkommentar, der auf eine Implementierungsdeklaration gesetzt wird, auf die Parametersymbole der implementierungsdeklaration unter Verwendung ihrer Parameternamen. Dies steht im Einklang mit partiellen Methoden.

/// <summary>
/// My type
/// </summary>
partial class C
{
    public partial int this[int x] { get; set; }

    /// <summary>
    /// <paramref name="x"/> // warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'x', but there is no parameter by that name
    /// <paramref name="y"/> // ok. 'Go To Definition' will go to 'int y'.
    /// </summary>
    public partial int this[int y] { get => 1; set { } } // warning CS9256: Partial property declarations 'int C.this[int x]' and 'int C.this[int y]' have signature differences.
}

Ergibt die folgende XML-Dokumentationsdatei:

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>ConsoleApp1</name>
    </assembly>
    <members>
        <member name="T:C">
            <summary>
            My type
            </summary>
        </member>
        <member name="P:C.Item(System.Int32)">
            <summary>
            <paramref name="x"/> // warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'x', but there is no parameter by that name
            <paramref name="y"/> // ok. 'Go To Definition' will go to 'int y'.
            </summary>
        </member>
    </members>
</doc>

Dies kann verwirrend sein, da die Metadatensignatur Parameternamen aus dem Definitionsteil verwendet. Es wird empfohlen, sicherzustellen, dass Parameternamen über Teile hinweg übereinstimmen, um diese Verwirrung zu vermeiden.

Indexer

Gemäß der LDM-Sitzung am 2. November 2022 werden Indexer mit dieser Funktion unterstützt.

Die Grammatik der Indexer wird wie folgt geändert:

indexer_declaration
-    : attributes? indexer_modifier* indexer_declarator indexer_body
+    : attributes? indexer_modifier* 'partial'? indexer_declarator indexer_body
-    | attributes? indexer_modifier* ref_kind indexer_declarator ref_indexer_body
+    | attributes? indexer_modifier* 'partial'? ref_kind indexer_declarator ref_indexer_body
    ;

Partielle Indexerparameter müssen in den Deklarationen nach denselben Regeln wie bei übereinstimmenden Signaturen übereinstimmen. Attributzusammenführung erfolgt über partielle Indexerparameter hinweg.

partial class C
{
    public partial int this[int x] { get; set; }
    public partial int this[int x]
    {
        get => this._store[x];
        set => this._store[x] = value;
    }
}

// attribute merging
partial class C
{
    public partial int this[[Attr1] int x]
    {
        [Attr2] get;
        set;
    }

    public partial int this[[Attr3] int x]
    {
        get => this._store[x];
        [Attr4] set => this._store[x] = value;
    }

    // results in a merged member emitted to metadata:
    public int this[[Attr1, Attr3] int x]
    {
        [Attr2] get => this._store[x];
        [Attr4] set => this._store[x] = value;
    }
}

Offene Probleme

Andere Mitgliedstypen

Ein Communitymitglied initiierte eine Diskussion, um Unterstützung für partielle Ereignisse anzufordern. In der LDM-Sitzung am 2. November 2022 beschlossen wir, von der Unterstützung für Ereignisse Abstand zu nehmen, auch weil zu diesem Zeitpunkt niemand eine solche angefordert hatte. Wir möchten diese Frage vielleicht noch einmal überprüfen, da diese Anfrage jetzt eingegangen ist, und sie ist seit der letzten Diskussion über ein Jahr vergangen.

Wir könnten auch noch weiter gehen und partielle Deklarationen von Konstruktoren, Operatoren, Feldern usw. zulassen, aber es ist unklar, ob der damit verbundene Aufwand für das Design gerechtfertigt ist, nur weil wir bereits partielle Eigenschaften haben.