Freigeben über


Partielle Methoden erweitern

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 Issue: https://github.com/dotnet/csharplang/issues/3301

Zusammenfassung

Dieser Vorschlag zielt darauf ab, alle Einschränkungen für die Signaturen von partial Methoden in C# zu entfernen. Ziel ist es, die Gruppe von Szenarien zu erweitern, in denen diese Methoden mit Quellgeneratoren arbeiten können, sowie ein allgemeineres Deklarationsformular für C#-Methoden zu sein.

Siehe auch die ursprüngliche Teilmethodenspezifikation (§15.6.9).

Motivation

C# hat eingeschränkte Unterstützung für Entwickler, die Methoden in Deklarationen und Definitionen/Implementierungen aufteilen.

partial class C
{
    // The declaration of C.M
    partial void M(string message);
}

partial class C
{
    // The definition of C.M
    partial void M(string message) => Console.WriteLine(message);
}

Ein Verhalten der partial-Methoden besteht darin, dass die Sprache bei fehlender Definition alle Aufrufe der partial-Methode einfach löscht. Im Wesentlichen verhält es sich wie ein Aufruf einer [Conditional]-Methode, bei der die Bedingung auf "false" ausgewertet wurde.

partial class D
{
    partial void M(string message);

    void Example()
    {
        M(GetIt()); // Call to M and GetIt erased at compile time
    }

    string GetIt() => "Hello World";
}

Die ursprüngliche Motivation für dieses Feature war die Quellgenerierung in Form von vom Designer generierten Code. Benutzer haben den generierten Code ständig bearbeitet, da sie einen Aspekt des generierten Codes verbinden wollten. Vor allem Teile des Windows Forms-Startprozesses, nachdem Komponenten initialisiert wurden.

Das Bearbeiten des generierten Codes war fehleranfällig, da jede Aktion, die den Designer veranlasste, den Code neu zu generieren, dazu führte, dass die Benutzerbearbeitung überschrieben wurde. Die Funktion der partial-Methode hat dieses Problem entschärft, da sie Designern die Möglichkeit bietet, Haken in Form von partial-Methoden zu setzen.

Designer könnten Hooks wie partial void OnComponentInit() ausgeben, und Entwickler könnten Deklarationen für sie definieren oder nicht definieren. In beiden Fällen würde der generierte Code kompiliert werden und Entwickler, die an dem Prozess interessiert sind, könnten sich bei Bedarf einbringen.

Dies bedeutet, dass partielle Methoden mehrere Einschränkungen haben:

  1. Muss einen void Rückgabetyp haben.
  2. Darf keine out Parameter haben.
  3. Darf keine Zugriffsfreiheit haben (implizit private).

Diese Einschränkungen sind vorhanden, da die Sprache Code ausgeben muss, wenn die Aufrufwebsite gelöscht wird. Da sie gelöscht werden können, ist private die einzig mögliche Zugriffsfreiheit, da das Mitglied in den Assembly-Metadaten nicht offengelegt werden kann. Diese Einschränkungen dienen auch dazu, die Gruppe von Szenarien einzuschränken, in denen partial Methoden angewendet werden können.

Der Vorliegende Vorschlag besteht darin, alle bestehenden Einschränkungen für partial Methoden zu entfernen. Lassen Sie sie im Wesentlichen out Parameter, nicht-leere Rückgabetypen oder jede Art von Zugriffsfreiheit haben. Diese partial Deklarationen hätten dann die zusätzliche Anforderung, dass eine Definition vorhanden sein muss. Dies bedeutet, dass die Sprache nicht die Auswirkungen des Löschens der Anrufwebsites berücksichtigen muss.

Dies würde den Satz von Generatorszenarien erweitern, an denen partial-Methoden teilnehmen könnten. Somit wird eine gute Verknüpfung mit unserer Quellgenerator-Funktion hergestellt. Beispielsweise könnte ein regex mithilfe des folgenden Musters definiert werden:

[RegexGenerated("(dog|cat|fish)")]
partial bool IsPetMatch(string input);

Dies bietet dem Entwickler eine einfache deklarative Möglichkeit, sich für Generatoren zu entscheiden, sowie den Generatoren einen sehr einfachen Satz von Deklarationen im Quellcode, um ihre generierte Ausgabe zu beeinflussen.

Vergleichen Sie dies mit der Schwierigkeit, die ein Generator hätte, um den folgenden Codeausschnitt anzuschließen.

var regex = new RegularExpression("(dog|cat|fish)");
if (regex.IsMatch(someInput))
{

}

Da der Compiler nicht zulässt, dass Generatoren in den Code einhaken und dieses Muster umsetzen, wäre es für Generatoren nahezu unmöglich. Sie müssten auf Reflexion in der IsMatch Implementierung zurückgreifen oder die Benutzenden auffordern, ihre Aufrufseiten auf eine neue Methode zu ändern + die Regex zu refactoring, um das Zeichenfolgeliteral als Argument zu übergeben. Es ist ziemlich chaotisch.

Detailentwurf

Die Sprache wird geändert, um zu ermöglichen, dass partial-Methoden mit einem expliziten Zugriffsmodifizierer annotiert werden. Dies bedeutet, dass sie als private, publicusw. bezeichnet werden können ...

Wenn eine partial-Methode einen expliziten Zugriffsmodifizierer hat, muss die Deklaration selbst dann eine entsprechende Definition haben, wenn die Zugänglichkeit privateist.

partial class C
{
    // Okay because no definition is required here
    partial void M1();

    // Okay because M2 has a definition
    private partial void M2();

    // Error: partial method M3 must have a definition
    private partial void M3();
}

partial class C
{
    private partial void M2() { }
}

Darüber hinaus wird die Sprache alle Beschränkungen aufheben, was in einer partial-Methode mit expliziter Zugriffsfreiheit erscheinen kann. Solche Deklarationen können nicht-leere Rückgabetypen, out Parameter, extern Modifizierer, etc. enthalten ... Diese Signaturen werden den vollen Ausdruck der Sprache C# haben.

partial class D
{
    // Okay
    internal partial bool TryParse(string s, out int i); 
}

partial class D
{
    internal partial bool TryParse(string s, out int i) { ... }
}

Dadurch können partial Methoden explizit an overrides und interface Implementierungen teilnehmen:

interface IStudent
{
    string GetName();
}

partial class C : IStudent
{
    public virtual partial string GetName(); 
}

partial class C
{
    public virtual partial string GetName() => "Jarde";
}

Der Compiler ändert den Fehler, der ausgegeben wird, wenn eine partial-Methode ein unzulässiges Element enthält, um im Wesentlichen zu sagen:

Kann nicht ref auf eine partial Methode anwenden, die keine explizite Zugriffsfreiheit hat

Dies hilft Entwicklern bei der Verwendung dieses Features in die richtige Richtung.

Einschränkungen:

  • partial Deklarationen mit expliziter Barrierefreiheit müssen über eine Definition verfügen.
  • partial Deklarationen und Definitionssignaturen müssen für alle Methoden- und Parametermodifizierer übereinstimmen. Die einzigen Aspekte, die sich unterscheiden können, sind Parameternamen und Attributlisten (dies ist nicht neu, sondern eine bestehende Anforderung von partial Methoden).

Fragen

partial für alle Mitglieder

Da wir partial erweitern, um den Quellcode-Generatoren entgegenzukommen, sollten wir es auch auf alle Mitglieder der Klasse ausweiten? Beispielsweise sollten wir partial-Konstruktoren, -Operatoren usw. deklarieren können.

Lösung Die Idee ist gut, aber zu diesem Zeitpunkt im Zeitplan von C# 9 versuchen wir, unnötige Funktionen zu vermeiden. Sie möchten das sofortige Problem der Erweiterung des Features lösen, um mit modernen Quellgeneratoren zu arbeiten.

Die Erweiterung von partial zur Unterstützung weiterer Mitglieder wird für die C# 10-Version berücksichtigt. Es scheint wahrscheinlich, dass wir diese Erweiterung in Betracht ziehen werden. Dies bleibt ein aktiver Vorschlag, aber es wurde noch nicht umgesetzt.

Verwenden von abstract anstelle von partial

Der Kernpunkt dieses Vorschlags besteht im Wesentlichen darin, sicherzustellen, dass eine Erklärung über eine entsprechende Definition/Implementierung verfügt. Sollten wir abstract verwenden, da es bereits ein Sprachschlüsselwort ist, das den Entwickler zwingt, über eine Implementierung nachzudenken?

Lösung Es gab eine lebhafte Diskussion darüber, aber letztendlich wurde es abgelehnt. Ja, die Anforderungen sind vertraut, aber die Konzepte unterscheiden sich erheblich. Könnte den Entwickler leicht dazu führen, zu glauben, dass sie virtuelle Slots erstellt haben, wenn sie dies nicht getan haben.