Classes et méthodes partielles (Guide de programmation C#)
Il est possible de fractionner la définition d’une classe, d’un struct, d’une interface ou d’une méthode entre plusieurs fichiers sources. Chaque fichier source contient une section de la définition de méthode ou de type, et toutes les parties sont combinées au moment où l’application est compilée.
Classes partielles
Il peut être utile de fractionner une définition de classe dans les situations suivantes :
- La déclaration d’une classe entre plusieurs fichiers distincts permet à plusieurs programmeurs d’y travailler simultanément.
- Vous pouvez ajouter du code à la classe sans avoir à recréer le fichier source qui contient la source générée automatiquement. Visual Studio suit cette approche pour créer des formulaires Windows Forms, du code wrapper de service web, etc. Vous pouvez écrire du code qui utilise ces classes sans avoir à modifier le fichier créé par Visual Studio.
- Les générateurs sources peuvent générer des fonctionnalités supplémentaires dans une classe.
Pour fractionner une définition de classe, utilisez le modificateur de mot clé partiel . Dans la pratique, chaque classe partielle est généralement définie dans un fichier distinct, ce qui facilite la gestion et l’expansion de la classe au fil du temps.
L’exemple suivant Employee
montre comment la classe peut être divisée entre deux fichiers : Employee_Part1.cs et Employee_Part2.cs.
// This is in Employee_Part1.cs
public partial class Employee
{
public void DoWork()
{
}
}
// This is in Employee_Part2.cs
public partial class Employee
{
public void GoToLunch()
{
}
}
//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.
Le mot clé partial
indique que d’autres parties de la classe, du struct ou de l’interface peuvent être définies dans l’espace de noms. Toutes les parties doivent utiliser le mot clé partial
. Toutes les parties doivent être disponibles à la compilation pour former le type final. Toutes les parties doivent avoir la même accessibilité : public
, private
, etc.
Si une partie est déclarée comme abstract, l’ensemble du type est considéré comme abstract. Si une partie est déclarée comme sealed, l’ensemble du type est considéré comme sealed. Si une partie déclare un type de base, l’ensemble du type hérite de cette classe.
Toutes les parties qui spécifient une classe de base doivent être en accord, mais les parties qui omettent une classe de base héritent tout de même du type de base. Les parties peuvent spécifier des interfaces de base différentes. Le type final implémente alors toutes les interfaces indiquées dans toutes les déclarations partielles. Tous les membres de classe, de struct ou d’interface déclarés dans une définition partielle sont disponibles pour toutes les autres parties. Le type final est la combinaison de toutes les parties au moment de la compilation.
Notes
Le modificateur partial
n’est pas disponible sur les déclarations de délégué ou d’énumération.
L’exemple suivant montre que les types imbriqués peuvent être partiels, même si le type dans lequel ils sont imbriqués n’est pas partiel lui-même.
class Container
{
partial class Nested
{
void Test() { }
}
partial class Nested
{
void Test2() { }
}
}
Au moment de la compilation, les attributs de définitions de type partiel sont fusionnés. Observez, par exemple, les déclarations suivantes :
[SerializableAttribute]
partial class Moon { }
[ObsoleteAttribute]
partial class Moon { }
Ils sont équivalents aux déclarations suivantes :
[SerializableAttribute]
[ObsoleteAttribute]
class Moon { }
Les éléments suivants sont fusionnés à partir de toutes les définitions de type partiel :
- Commentaires XML. Cependant, si les deux déclarations d’un membre partiel incluent des commentaires, seuls les commentaires du membre implémentant sont inclus.
- interfaces
- attributs de paramètre de type générique
- attributs de classe
- membres
Observez, par exemple, les déclarations suivantes :
partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }
Ils sont équivalents aux déclarations suivantes :
class Earth : Planet, IRotate, IRevolve { }
Restrictions
Il y a plusieurs règles à respecter quand vous utilisez des définitions de classe partielle :
- Toutes les définitions de type partiel conçues comme des parties du même type doivent être modifiées avec
partial
. Par exemple, les déclarations de classe suivantes génèrent une erreur :public partial class A { } //public class A { } // Error, must also be marked partial
- Le modificateur
partial
peut uniquement être placé juste avant le mot cléclass
,struct
ouinterface
. - Les types partiels imbriqués sont autorisés dans les définitions de type partiel, comme illustré dans l’exemple suivant :
partial class ClassWithNestedClass { partial class NestedClass { } } partial class ClassWithNestedClass { partial class NestedClass { } }
- Toutes les définitions de type partiel conçues comme des parties du même type doivent être définies dans le même assembly et dans le même module (fichier .exe ou .dll). Les définitions partielles ne peuvent pas être fractionnées entre plusieurs modules.
- Le nom de classe et les paramètres de type générique doivent correspondre dans toutes les définitions de type partiel. Les types génériques peuvent être partiels. Chaque déclaration partielle doit utiliser les mêmes noms de paramètres, dans le même ordre.
- Les mots-clés suivants dans une définition de type partiel sont facultatifs, mais s’ils sont présents dans une définition de type partiel, ils doivent également être spécifiés dans l’autre définition partielle pour le même type :
Pour plus d’informations, consultez Contraintes sur les paramètres de type.
Exemples
Dans l’exemple suivant, les champs et le constructeur de la Coords
classe sont déclarés dans une définition de classe partielle (Coords_Part1.cs
) et la PrintCoords
méthode est déclarée dans une autre définition de classe partielle (Coords_Part2.cs
). Cette séparation montre comment les classes partielles peuvent être divisées entre plusieurs fichiers pour faciliter la maintenance.
// 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
L’exemple suivant montre que vous pouvez également développer des interfaces et des structs partiels.
partial interface ITest
{
void Interface_Test();
}
partial interface ITest
{
void Interface_Test2();
}
partial struct S1
{
void Struct_Test() { }
}
partial struct S1
{
void Struct_Test2() { }
}
Membres partiels
Une classe ou une structure partielle peut contenir un membre partiel. Une partie de la classe contient la signature du membre. Une implémentation peut être définie dans la même partie ou dans une autre partie.
Une implémentation n’est pas nécessaire pour une méthode partielle lorsque la signature obéit aux règles suivantes :
- La déclaration n’inclut aucun modificateur d’accès. La méthode dispose d’un accès
private
par défaut. - Le type de retour est
void
. - Aucun des paramètres n’a le modificateur
out
. - La déclaration de méthode ne peut inclure aucun des modificateurs suivants :
La méthode et tous les appels à cette méthode sont supprimés au moment de la compilation en cas d’absence d’implémentation.
Toute méthode qui ne respecte pas toutes ces restrictions, y compris les propriétés et indexeurs, doit fournir une implémentation. Cette implémentation peut être fournie par un générateur de sources. Les propriétés partielles ne peuvent pas être implémentées à l’aide de propriétés implémentées automatiquement. Le compilateur ne peut pas faire la distinction entre une propriété implémentée automatiquement et la déclaration déclarante d’une propriété partielle.
Les méthodes partielles permettent à l’implémenteur d’une partie d’une classe de déclarer un membre. L’implémenteur d’une autre partie de la classe peut définir ce membre. Cette séparation peut être utile dans les deux cas suivants : pour des modèles qui génèrent du code réutilisable et pour des générateurs de sources.
- Code du modèle : le modèle réserve un nom et une signature de méthode afin que le code généré puisse appeler la méthode. Ces méthodes suivent les restrictions qui permettent à un développeur de décider s’il doit implémenter la méthode. Si la méthode n’est pas implémentée, le compilateur supprime la signature de méthode et tous les appels à la méthode. Les appels à la méthode, y compris tous les résultats retournés par l’évaluation des arguments dans les appels, n’ont aucun effet au moment de l’exécution. C’est pourquoi le code dans la classe partielle peut utiliser librement une méthode partielle, même si l’implémentation n’est pas fournie. Aucune erreur de compilation ou d’exécution ne survient si la méthode est appelée, sans finalement être implémentée.
- Générateurs de code source : Les générateurs de code source fournissent une implémentation pour les membres. Le développeur humain peut ajouter la déclaration du membre (souvent avec des attributs lus par le générateur de code source). Le développeur peut écrire du code qui appelle ces membres. Le générateur de sources s’exécute au moment de la compilation et fournit l’implémentation. Dans ce scénario, les restrictions pour les membres partiels qui pourraient ne pas être implémentés ne sont souvent pas suivies.
// Definition in file1.cs
partial void OnNameChanged();
// Implementation in file2.cs
partial void OnNameChanged()
{
// method body
}
- Les déclarations de membres partiels doivent commencer par le mot-clé contextuel partial.
- Les signatures des membres partiels dans les deux parties du type partiel doivent correspondre.
- Un membre partiel peut avoir des modificateurs static et unsafe.
- Un membre partiel peut être générique. Les contraintes doivent être identiques quant à la déclaration de la méthode de définition et d’implémentation. Les noms de paramètre et de paramètre de type ne doivent pas obligatoirement être identiques dans la déclaration d’implémentation et la déclaration de définition.
- Vous pouvez créer un délégué pour une méthode partielle définie et implémentée, mais pas pour une méthode partielle sans implémentation.
Spécification du langage C#
Pour plus d’informations, consultez Types partiels et méthodes partielles dans la spécification du langage C#. La spécification du langage est la source de référence pour la syntaxe C# et son utilisation. Les fonctionnalités supplémentaires des méthodes partielles sont définies dans la spécification de la fonctionnalité.