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 partiellen Schlüsselwortmodifizierer. In der Praxis wird jede partielle Klasse in der Regel in einer separaten Datei definiert, wodurch es einfacher ist, die Klasse im Laufe der Zeit zu verwalten und zu erweitern.
Im folgenden Employee
Beispiel wird veranschaulicht, wie die Klasse in zwei Dateien unterteilt werden kann: Employee_Part1.cs und Employee_Part2.cs.
// This is in Employee_Part1.cs
public partial class Employee
{
public void DoWork()
{
Console.WriteLine("Employee is working.");
}
}
// This is in Employee_Part2.cs
public partial class Employee
{
public void GoToLunch()
{
Console.WriteLine("Employee is at lunch.");
}
}
//Main program demonstrating the Employee class usage
public class Program
{
public static void Main()
{
Employee emp = new Employee();
emp.DoWork();
emp.GoToLunch();
}
}
// Expected Output:
// Employee is working.
// Employee is at lunch.
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. Wenn jedoch beide Deklarationen eines Teilmitglieds Kommentare enthalten, werden nur die Kommentare aus dem implementierenden Mitglied einbezogen.
- Clusterverwaltung
- 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örternclass
,struct
oderinterface
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 folgenden Schlüsselwörter in einer Teil-Typ-Definition sind optional, aber wenn sie in einer Teil-Typ-Definition vorhanden sind, müssen sie auch in anderen Teil-Definitionen für denselben Typ angegeben werden:
Weitere Informationen finden Sie unter Einschränkungen für Typparameter.
Beispiele
Im folgenden Beispiel werden die Felder und der Konstruktor der Coords
Klasse in einer partiellen Klassendefinition (Coords_Part1.cs
) deklariert, und die PrintCoords
Methode wird in einer anderen partiellen Klassendefinition (Coords_Part2.cs
) deklariert. Diese Trennung veranschaulicht, wie partielle Klassen auf mehrere Dateien aufgeteilt werden können, um die Wartung zu erleichtern.
// This is in Coords_Part1.cs
public partial class Coords
{
private int x;
private int y;
public Coords(int x, int y)
{
this.x = x;
this.y = y;
}
}
// This is in Coords_Part2.cs
public partial class Coords
{
public void PrintCoords()
{
Console.WriteLine("Coords: {0},{1}", x, y);
}
}
// Main program demonstrating the Coords class usage
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() { }
}
Teilmitglieder
Eine partielle Klasse oder Struktur kann ein Teilmitglied enthalten. Ein Teil der Klasse enthält die Signatur des Mitglieds. 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 (einschließlich Eigenschaften und Indexern), muss eine Implementierung bereitstellen. Diese Implementierung kann von einem Quellgenerator bereitgestellt werden. Partielle Eigenschaften können nicht mithilfe automatisch implementierter Eigenschaften implementiert werden. Der Compiler kann nicht zwischen einer automatisch implementierten Eigenschaft und der deklarierenden Deklaration einer partiellen Eigenschaft unterscheiden.
Ab C# 13 kann die Implementierungsdeklaration für eine partielle Eigenschaft feldbezogene Eigenschaften verwenden, um die Implementierungsdeklaration zu definieren. Eine feldgesicherte Eigenschaft bietet eine präzise Syntax, bei der das field
Schlüsselwort auf das kompilierte Sicherungsfeld für die Eigenschaft zugreift. Sie können beispielsweise Folgendes schreiben:
// in file1.cs
public partial class PropertyBag
{
// Defining declaration
public partial int MyProperty { get; set; }
}
// In file2.cs
public partial class PropertyBag
{
// Defining declaration
public partial int MyProperty { get => field; set; }
}
Sie können entweder im Accessor oder set
in beiden get
Anwendungen verwendenfield
.
Wichtig
Das field
Schlüsselwort ist ein Vorschaufeature in C# 13. Sie müssen .NET 9 verwenden und das <LangVersion>
Element preview
in Der Projektdatei festlegen, um das field
Kontextschlüsselwort zu verwenden.
Achten Sie darauf, die field
Schlüsselwortfunktion in einer Klasse zu verwenden, die ein Feld mit dem Namen field
hat. Das neue field
Schlüsselwort schattiert ein Feld, das im Bereich eines Eigenschaftenaccessors benannt field
ist. Sie können entweder den Namen der field
Variablen ändern oder das @
Token verwenden, um auf den field
Bezeichner zu verweisen als @field
. Weitere Informationen erhalten Sie, indem Sie die Featurespezifikation für das field
Schlüsselwort lesen.
Mit partiellen Methoden kann der Implementierer des einen Teils einer Klasse ein Mitglied deklarieren. Der Implementierer eines anderen Teils der Klasse kann das betreffende Mitglied 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 Mitglieder bereit. Der menschliche Entwickler kann die Mitgliedsdeklaration hinzufügen (häufig mit Attributen, die vom Quellgenerator gelesen werden). Der Entwickler kann Code schreiben, der diese Mitglieder aufruft. Der Quellgenerator wird während der Kompilierung ausgeführt und stellt die Implementierung bereit. In diesem Szenario werden die Einschränkungen für Teilmitglieder, 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
}
- Teilmitglieddeklarationen müssen mit dem Kontextschlüsselwort partial beginnen.
- Teilmitgliedsignaturen in beiden Teilen des partiellen Typs müssen übereinstimmen.
- Teilmitglieder können statische und unsichere Modifizierer besitzen.
- Teilmitglieder 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 neuen Features für partielle Methoden werden in der Featurespezifikation definiert.