Le mot-clé partial en C# permet de fragmenter la définition d'une classe, d'une structure ou d'une interface à travers plusieurs fichiers sources. Cette approche est particulièrement bénéfique pour les projets d'envergure nécessitant le travail simultané de plusieurs développeurs, ou pour isoler proprement le code généré automatiquement par des outils du code métier écrit manuellement.
Fragmentation des types
Lorsqu'un type est marqué comme partiel, le compilateur agrège toutes ses définitions lors de la compilation pour former une seule entité logique. Toutes les parties doivent obligatoirement utiliser le modificateur partial.
Voici une illustration avec une classe NetworkClient séparée en deux fichiers distincts :
// Fichier Connection.cs
namespace NetworkLayer
{
public partial class NetworkClient
{
private string _endpoint;
public void Connect(string url)
{
_endpoint = url;
// Logique d'initialisation de la connexion
}
}
}
// Fichier DataTransfer.cs
namespace NetworkLayer
{
public partial class NetworkClient
{
public void SendPayload(byte[] data)
{
// Logique d'envoi des données utilisant _endpoint
}
}
}
Mécanisme de fusion à la compilation
Les attributs, les interfaces implémentées, les commentaires XML et les contraintes génériques définis dans différentes parties sont combinés automatiquement par le compilateur.
Par exemple, la déclaration d'attributs :
[Serializable]
public partial class DataCache { }
[Obsolete("Veuillez utiliser le nouveau système de cache.")]
public partial class DataCache { }
Est strictement équivalente à :
[Serializable]
[Obsolete("Veuillez utiliser le nouveau système de cache.")]
public class DataCache { }
De même pour les héritages et les interfaces :
public partial class ElectricCar : Vehicle, IChargeable { }
public partial class ElectricCar : IAutonomous { }
Le type final compilé héritera de Vehicle et implémentera à la fois IChargeable et IAutonomous.
Règles et contraintes des types partiels
- Toutes les définitions doivent utiliser le modificateur
partial. Omettre ce mot-clé sur l'une des parties génèrera une erreur de compilation. - Le modificateur doit précéder immédiatement les mots-clés
class,structouinterface. - Toutes les parties doivent résider dans le même assembly et le même module (le même fichier .dll ou .exe). La fragmentation ne peut pas traverser les limites des modules.
- Les noms de types et les paramètres génériques doivent correspondre exactement dans chaque déclaration. Les paramètres génériques doivent être dans le même ordre.
- Les modificateurs d'acccessibilité (
public,private, etc.), ainsi que les mots-clés commeabstractousealed, s'appliquent au type complet. S'ils sont spécifiés dans plusieurs parties, ils ne doivent pas entrer en conflit. - Le modificateur
partialne peut pas être appliqué aux déclarations de délégués ou d'énumérations. - Les types imbriqués peuvent également être déclarés comme partiels, même si le type englobant ne l'est pas.
Exemple de type imbriqué partiel :
public class GameEngine
{
public partial class PhysicsComponent
{
public void CalculateGravity() { }
}
}
public partial class GameEngine
{
public partial class PhysicsComponent
{
public void ApplyFriction() { }
}
}
Les méthodes partielles
Une méthode partielle définit sa signature dans une partie d'un type partiel, et son implémentation (qui est optionnelle) dans une autre. Si l'implémentation est omise, le compilateur supprime purement et simplement la signature et tous les appels à cette méthode. Cela évite toute surcharge à l'exécution, car l'apppel et l'évaluation de ses paramètres sont complètement éliminés.
Ce mécanisme est idéal pour créer des points d'ancrage (hooks) dans du code généré automatiquement, permettant aux développeurs d'injecter du code personnalisé sans modifier les fichiers générés.
Caractéristiques des méthodes partielles
- Elles doivent impérativement retourner
void. - Elles sont implicitement
privateet ne peuvent pas être déclarées commevirtual. - Elles peuvent accepter des paramètres
ref, mais les paramètresoutsont interdits. - Elles ne peuvent pas être marquées comme
extern, car c'est la présence du corps de la méthode qui détermine si elle est implémentée ou non. - Les modificateurs
staticetunsafesont autorisés. - Elles peuvent être génériques. Les contraintes doivent être définies sur la signature de définition, et peuvent être répétées sur l'implémentation.
- Il est possible de créer un délégué pointant vers une méthode partielle uniquement si celle-ci a été effectivement implémentée.
Exemple d'utilisation d'une méthode partielle dans un contexte de traitement de commandes :
// Fichier OrderDefinition.cs
namespace Commerce
{
public partial class OrderProcessor
{
public void ProcessOrder(int orderId)
{
ValidateOrder(orderId);
OnOrderValidated(orderId); // Appel de la méthode partielle
FinalizeOrder(orderId);
}
// Signature de la méthode partielle
partial void OnOrderValidated(int id);
}
}
// Fichier OrderImplementation.cs
namespace Commerce
{
public partial class OrderProcessor
{
// Implémentation optionnelle de la méthode partielle
partial void OnOrderValidated(int id)
{
Console.WriteLine($"Validation terminée avec succès pour la commande {id}.");
}
}
}
Si la méthode OnOrderValidated n'est pas implémentée dans le second fichier, le compilateur ignorera sa déclaration et supprimera l'appel dans ProcessOrder, sans générer la moindre erreur de compilation ou d'exécution.