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é partial, comme ci-dessous :

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

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

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
  • 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 ou interface.
  • 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 sont facultatifs dans une définition de type partiel. S’ils sont utilisés dans une définition de type partiel, ils ne doivent pas être en conflit avec les mots clés spécifiés dans une 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 classe Coords sont déclarés dans une définition de classe partielle, et le membre PrintCoords est déclaré dans une autre définition de classe partielle.

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

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() { }
}

Méthodes partielles

Une classe partielle ou un struct partiel peut contenir une méthode partielle. Une partie de la classe contient la signature de la méthode. 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.

Les méthodes qui ne sont pas conformes à toutes ces restrictions (par exemple, la méthode public virtual partial void) doivent fournir une implémentation. Cette implémentation peut être fournie par un générateur de sources.

Les méthodes partielles permettent à l’implémenteur d’une partie d’une classe de déclarer une méthode. L’implémenteur d’une autre partie de la classe peut définir cette méthode. 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 sources : les générateurs de sources fournissent une implémentation pour les méthodes. Le développeur humain peut ajouter la déclaration de méthode (souvent avec des attributs lus par le générateur de sources). Le développeur peut écrire du code qui appelle ces méthodes. 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 relatives aux méthodes partielles, impossibles à implémenter, ne sont souvent pas respectées.
// Definition in file1.cs
partial void OnNameChanged();

// Implementation in file2.cs
partial void OnNameChanged()
{
  // method body
}
  • Les déclarations de méthode partielle doivent commencer par le mot clé contextuel partial.
  • Les signatures de méthode partielle figurant dans les deux parties du type partial doivent correspondre.
  • Les méthodes partielles peuvent avoir des modificateurs static et unsafe.
  • Les méthodes partielles peuvent être génériques. 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é.

Voir aussi