Partielle Klassen und Methoden (C#-Programmierhandbuch)

Es ist möglich, die Definition einer Klasse, einer Struktur, einer Schnittstelle oder einer Methode auf zwei oder mehr Quelldateien aufzuteilen. Jede Quelldatei enthält einen Abschnitt der Typ- oder Methodendefinition. Die Teile werden bei der Kompilierung der Anwendung miteinander kombiniert.

Teilklassen

Es gibt mehrere Situationen, bei denen das Aufteilen einer Klassendefinition wünschenswert ist:

  • Durch das Deklarieren einer Klasse über separate Dateien können mehrere Programmierer gleichzeitig daran arbeiten.
  • Sie können der Klasse Code hinzufügen, ohne die Quelldatei neu erstellen zu müssen, die eine automatisch generierte Quelle enthält. Visual Studio verwendet diesen Ansatz, wenn es Windows Forms, Webdienst-Wrappercode usw. erstellt. Sie können Code erstellen, der diese Klassen verwendet, ohne die von Visual Studio erstellte Datei ändern zu müssen.
  • Quellgeneratoren können zusätzliche Funktionen in einer Klasse generieren.

Um eine Klassendefinition aufzuteilen, verwenden Sie den Schlüsselwortmodifizierer partial, wie hier gezeigt wird:

public partial class Employee
{
    public void DoWork()
    {
    }
}

public partial class Employee
{
    public void GoToLunch()
    {
    }
}

Das Schlüsselwort partial gibt an, dass andere Teile der Klasse, Struktur oder Schnittstelle im Namespace definiert werden können. Alle Teile müssen das Schlüsselwort partial verwenden. Alle Teile müssen zur Kompilierzeit verfügbar sein, um den endgültigen Typ zu bilden. Alle Teile müssen die gleiche Zugriffsebene haben, z.B. public, private usw.

Wenn ein beliebiger Teil als abstrakt deklariert wird, wird anschließend der ganze Typ als abstrakt betrachtet. Wenn ein beliebiges Teil als versiegelt deklariert wird, wird anschließend der ganze Typ als versiegelt betrachtet. Wenn ein beliebiger Teil einen Basistyp deklariert, erbt der ganze Typ diese Klasse.

Alle Teile, die eine Basisklasse angeben, müssen übereinstimmen, aber Teile, die eine Basisklasse auslassen, erben weiterhin den Basistyp. Teile können unterschiedliche Basisschnittstellen angeben, und der letzte Typ implementiert alle Schnittstellen, die von allen Teildeklarationen aufgeführt sind. Alle Klassen-, Struktur- und Schnittstellenmember, die in einer Teildefinition deklariert wurden, sind für alle anderen Teile verfügbar. Der endgültige Typ besteht aus allen Teilen zur Kompilierzeit.

Hinweis

Der partial-Modifizierer ist nicht für Delegat- oder Enumerationsdeklarationen verfügbar.

Das folgende Beispiel zeigt, dass geschachtelte Typen partiell sein können, auch wenn der Typ, in dem sie geschachtelt sind, nicht selbst partiell ist.

class Container
{
    partial class Nested
    {
        void Test() { }
    }

    partial class Nested
    {
        void Test2() { }
    }
}

Zur Kompilierzeit werden Attribute von partiellen Typdefinitionen zusammengeführt. Betrachten Sie beispielsweise die folgenden Deklarationen:

[SerializableAttribute]
partial class Moon { }

[ObsoleteAttribute]
partial class Moon { }

Sie entsprechen den folgenden Deklarationen:

[SerializableAttribute]
[ObsoleteAttribute]
class Moon { }

Die folgenden werden aus allen partiellen Typdefinitionen zusammengeführt:

  • XML-Kommentare
  • Schnittstellen
  • Generische Parameterattribute
  • Klassenattribute
  • Member

Betrachten Sie beispielsweise die folgenden Deklarationen:

partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }

Sie entsprechen den folgenden Deklarationen:

class Earth : Planet, IRotate, IRevolve { }

Beschränkungen

Es sind mehrere Regeln zu beachten, wenn Sie partielle Klassendefinitionen verwenden:

  • Alle partiellen Typdefinitionen, die als Teile des gleichen Typs vorgesehen sind, müssen mit partial geändert werden. Die folgenden Klassendeklarationen erzeugen z.B. einen Fehler:
    public partial class A { }
    //public class A { }  // Error, must also be marked partial
    
  • Der partial-Modifizierer kann nur unmittelbar vor den Schlüsselwörtern class, struct oder interface erscheinen.
  • Geschachtelte partielle Typen sind in partiellen Typdefinitionen zulässig, wie im folgenden Beispiel dargestellt wird:
    partial class ClassWithNestedClass
    {
        partial class NestedClass { }
    }
    
    partial class ClassWithNestedClass
    {
        partial class NestedClass { }
    }
    
  • Alle partiellen Typdefinitionen, die als Teile des gleichen Typs vorgesehen sind, müssen in der gleichen Assembly und demselben Modul (EXE- oder DLL-Datei) definiert werden. Partielle Definitionen können nicht mehrere Module umfassen.
  • Der Klassenname und die generischen Typparameter müssen mit allen partiellen Typdefinitionen übereinstimmen. Generische Typen können partiell sein. Jede partielle Definition muss die gleichen Parameternamen in der gleichen Reihenfolge aufweisen.
  • Die nachfolgenden Schlüsselwörter für eine partielle Typdefinition sind optional, wenn sie aber für eine partielle Definition vorhanden sind, können sie nicht mit anderen Schlüsselwörtern in Konflikt treten, die in anderen partiellen Definitionen desselben Typs angegeben wurden:

Weitere Informationen finden Sie unter Einschränkungen für Typparameter.

Beispiele

Im folgenden Beispiel werden die Felder und der Konstruktor der Klasse (Coords) in einer partiellen Klassendefinition deklariert, und der Member (PrintCoords) wird in einer anderen partiellen Klassendefinition deklariert.

public partial class Coords
{
    private int x;
    private int y;

    public Coords(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

public partial class Coords
{
    public void PrintCoords()
    {
        Console.WriteLine("Coords: {0},{1}", x, y);
    }
}

class TestCoords
{
    static void Main()
    {
        Coords myCoords = new Coords(10, 15);
        myCoords.PrintCoords();

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
// Output: Coords: 10,15

Im folgenden Beispiel wird gezeigt, dass Sie auch partielle Strukturen und Schnittstellen entwickeln können.

partial interface ITest
{
    void Interface_Test();
}

partial interface ITest
{
    void Interface_Test2();
}

partial struct S1
{
    void Struct_Test() { }
}

partial struct S1
{
    void Struct_Test2() { }
}

Partielle Methoden

Eine partielle Klasse oder Struktur kann eine partielle Methode enthalten. Ein Teil der Klasse enthält die Signatur der Methode. Eine Implementierung kann im gleichen oder in einem anderen Teil definiert werden.

Eine Implementierung ist für eine partielle Methode nicht erforderlich, wenn die Signatur den folgenden Regeln entspricht:

  • Die Deklaration enthält keine Zugriffsmodifizierer. Die Methode hat standardmäßig Zugriff auf private.
  • Der Rückgabetyp ist void.
  • Keiner der Parameter hat den out-Modifizierer.
  • Die Methodendeklaration kann keine der folgenden Modifizierer enthalten:

Die Methode und alle Aufrufe der Methode werden zur Kompilierungszeit entfernt, wenn keine Implementierung vorhanden ist.

Jede Methode, die nicht allen Einschränkungen entspricht (z. B. die public virtual partial void-Methode), muss eine Implementierung bereitstellen. Diese Implementierung kann von einem Quellgenerator bereitgestellt werden.

Mit partiellen Methoden kann der Implementierer des einen Teils einer Klasse eine Methode deklarieren. Der Implementierer eines anderen Teils der Klasse kann die betreffende Methode definieren. Es gibt zwei Szenarien, in denen diese Trennung nützlich ist: Vorlagen, die Codebausteine generieren, und Quellgeneratoren.

  • Vorlagencode: Die Vorlage reserviert einen Methodennamen und eine Signatur, damit generierter Code die Methode aufrufen kann. Diese Methode folgt den Einschränkungen, die es einem Entwickler ermöglichen, zu entscheiden, ob sie implementiert werden soll. Wenn die Methode nicht implementiert wird, kann der Compiler anschließend die Methodensignatur und alle Aufrufe an die Methode entfernen. Die Aufrufe an die Methode, einschließlich Ergebnissen, die bei der Auswertung eines Arguments im Aufruf auftreten würden, haben zur Laufzeit keine Auswirkung. Deshalb kann jeder Code in der partiellen Klasse eine partielle Methode frei verwenden, selbst wenn die Implementierung nicht bereitgestellt wird. Es ergeben sich keine Laufzeit- oder Kompilierzeitfehler, wenn die Methode aufgerufen, aber nicht implementiert wird.
  • Quellgeneratoren: Quellgeneratoren stellen eine Implementierung für Methoden bereit. Der menschliche Entwickler kann die Methodendeklaration hinzufügen (häufig mit Attributen, die vom Quellgenerator gelesen werden). Der Entwickler kann Code schreiben, der diese Methoden aufruft. Der Quellgenerator wird während der Kompilierung ausgeführt und stellt die Implementierung bereit. In diesem Szenario werden die Einschränkungen für partielle Methoden, die möglicherweise nicht implementiert werden, häufig nicht befolgt.
// Definition in file1.cs
partial void OnNameChanged();

// Implementation in file2.cs
partial void OnNameChanged()
{
  // method body
}
  • Partielle Methodendeklarationen müssen mit dem Kontextschlüsselwort partial beginnen.
  • Partielle Methodensignaturen in beiden Teilen des partiellen Typs müssen übereinstimmen.
  • Partielle Methoden können statische und unsichere Modifizierer besitzen.
  • Partielle Methoden können generisch sein. Einschränkungen müssen bei der definierenden und implementierenden Methodendeklaration identisch sein. Parameter- und Typparameternamen müssen in der implementierenden und definierenden Deklaration nicht gleich sein.
  • Sie können einen Delegat zu einer partiellen Methode definieren und implementieren, aber nicht an eine partielle Methode, die keine Implementierung hat.

C#-Programmiersprachenspezifikation

Weitere Informationen finden Sie unter Partielle Typen und Partielle Methoden in der C#-Sprachspezifikation. Die Sprachspezifikation ist die verbindliche Quelle für die Syntax und Verwendung von C#. Die zusätzlichen Features für partielle Methoden werden in der Featurespezifikation definiert.

Weitere Informationen