Pourquoi utiliser des classes partielles?

72

À mon sens, le partialmot-clé ne fait que permettre à une classe d'être divisée en plusieurs fichiers source. Y a-t-il une raison de faire cela autre que pour l'organisation du code? Je l'ai vu utilisé pour cela dans les classes d'interface utilisateur générées.

Cela semble une mauvaise raison de créer un mot clé complet. Si une classe est assez grande pour nécessiter plusieurs fichiers, elle en fait probablement trop. Je pensais que vous pourriez peut-être l'utiliser pour définir partiellement une classe pour un autre programmeur, mais il serait préférable de créer une classe abstraite.

Michael K
la source
10
Ce n'est pas vraiment un "mot clé entier" non plus. C'est un mot clé contextuel . partialsignifie seulement quelque chose quand il vient avant class. Vous pouvez l'utiliser comme identifiant dans d'autres parties du code, etc.
Dean Harding
4
Si vous les utilisez et que ce n'est pas pour le code généré, je vous déteste. Pas vous personnellement, mais vous en général. Je déteste chercher du code dans des classes partielles.
Steven Evers
3
Il n’est pas difficile de "rechercher du code", toutes les méthodes de classe apparaissent toujours dans le menu déroulant de VS, quelle que soit la partie de la partie que vous regardez. La navigation dans d'autres codes fonctionne également très bien (navigation "intelligente" dans le style R # ou bonne vieille touche shift-ctrl-f
Steve
2
C'est un malentendu que des classes partielles doivent être dans des fichiers séparés.
Bart

Réponses:

110

Il est très utile dans tous les scénarios où une partie de la classe est générée par un outil personnalisé, car elle vous permet d'ajouter une logique personnalisée au code généré sans hériter de la classe générée. Btw. il existe également des méthodes partielles pour la même raison.

Cela ne concerne pas uniquement l'interface utilisateur, mais également d'autres technologies telles que Linq-To-Sql ou Entity Framework.

Ladislav Mrnka
la source
44
En outre, il vous permet de garder votre code sous contrôle de version tout en laissant le code généré en dehors du contrôle de version.
Frank Shearar
3
Bon point, je n'ai jamais pensé à cela. Je l’ai seulement vu maltraité dans la pratique. Un moyen pour les programmeurs (médiocres) de garder la taille du fichier gérable.
Martin Wickman
1
Les meilleurs exemples que j'ai utilisés sont les suivants: a) pour les contextes de données Linq à SQL dans lesquels vous souhaitez étendre les classes; b) pour étendre les classes passées des services Web. Dans aucun cas, il ne s'agit ni d'un abus ni d'un moyen de garder la taille des fichiers gérables ... Je serais radicalement mécontent de le voir utilisé de cette manière (et je prendrais des mesures ...)
Murph le
3
Les classes WinForm l'utilisent pour masquer tout le code du concepteur créant des contrôles (ce qui est particulièrement important car le concepteur aime réorganiser de manière aléatoire tout le code qu'il crée, ce qui entraîne des dégâts énormes lorsque diff / blaming), et si vous utilisez XML, l'outil XSD peut créez à nouveau des classes partielles à partir du fichier de schéma, ce qui vous permet de séparer votre code du tri généré automatiquement.
Dan Neely
2
@ MartinWickman pas nécessairement moche. C'est un bon point de départ pour la refactorisation des objets divins dans le code hérité. Essayez de clarifier le paysage en séparant d’abord les grandes classes en fichiers séparés. (Je sais que votre commentaire est très ancien)
Konrad Morawski
26

Comme vous le dites, il est souvent utilisé pour séparer le code généré. Cela n'a souvent rien à voir avec la taille des classes / fichiers.

L’avantage de séparer le code généré en est un de style. Le code généré peut être assez moche et illisible et échouerait pour de nombreuses normes de codage (et contrôles StyleCop), mais ce n’est pas grave, personne n’a à le lire ni à le gérer directement. Ainsi, si vous le "cachez" dans un autre fichier, vous pouvez vous assurer que le reste de la classe est conforme aux normes, passe les contrôles StyleCop, etc.

Un autre domaine dans lequel j’ai utilisé c’est le cas où une classe implémente plusieurs interfaces. Il peut être très agréable de séparer l’implémentation en fichiers séparés, bien que ce soit plus une question de préférence personnelle, je n’ai jamais vu aucune norme de codage l’imposer ( ou empêcher) cela.

Steve
la source
9
Je n'avais jamais pensé à utiliser des partiels de classe pour séparer les implémentations d'interface, c'est une excellente idée.
Mark Booth
C'est beaucoup plus que du style. Les développeurs d'un générateur de code devraient passer à travers des étapes pour vous permettre d'éditer du code dans un fichier géré par le générateur, et même dans ce cas, cela poserait problème. L'avantage n ° 1, à mes yeux, est de vous donner un endroit pour écrire du code que le générateur va / ne peut pas toucher. C'est un avantage assez tangible.
Daniel
19

L’un des développeurs où je travaille a été très utile il y a quelques semaines pour la refactorisation d’énormes classes divines incontrôlables et dotées de nombreuses méthodes publiques: en séparant les fonctions logiques de chaque la classe dans une classe partielle séparée, vous pouvez physiquement séparer la classe en unités plus atomiques qui devraient être les classes sans casser aucune fonctionnalité existante, ce qui vous permet de voir ce qui est commun et ce qui ne l’est pas. Avec cette première étape, vous pouvez plus facilement diviser les partiels en leurs propres classes indépendantes et les implémenter dans la base de code. Je pensais que c'était une bonne idée.

Cependant, en général, je pense qu'ils ne devraient être utilisés que pour augmenter les classes générées par la machine lors de l'écriture de nouveau code.


la source
1
Je peux voir comment cela pourrait être pratique. Que ce soit une justification pour que ce soit dans la langue ou pas ... Je ne sais pas. Le code généré par la machine est une autre histoire, cependant.
Michael K
2
Oui, ce n'est pas une justification pour que ce soit dans la langue, mais c'est une façon intéressante de les utiliser.
18

Je peux penser à quelques scénarios utiles où les partiels ont un sens, la plupart d’entre eux que je m’utilise moi-même dans mes projets:

  • Pour séparer le code généré par Tool / IDE / Designer du code que vous gérez. Un bon exemple est le Form.Designer.csfichier contenant le code généré par le concepteur pour les applications Windows Form. De nombreux autres formats .NET ont également un code généré par un outil, qui peut être potentiellement régénéré lorsque vous générez votre projet, de sorte que toutes vos modifications personnalisées seront effacées. La séparation vous aidera à protéger votre code et vos modifications de telles modifications automatisées.

  • Lorsque vous implémentez plusieurs interfaces avec beaucoup de code dans l'implémentation. J'ai tendance à utiliser un fichier partiel séparé pour chaque interface, nommant comme ceci: {Class}.{Interface}.cs. Il est facile pour moi de voir dans l'EDI quelles interfaces {Class}implémentent et comment.

  • Lorsque la classe doit contenir une ou plusieurs classes imbriquées , en particulier avec suffisamment de code pour être placées dans un fichier séparé. Je voudrais coller au modèle ci-dessus et utiliser la {Class}.{NestedClass}.csconvention de nommage pour chaque classe imbriquée. Une réponse similaire est déjà mentionnée dans la réponse de Virtlink .

  • Quand j'écris des staticcours qui vont contenir des méthodes d'extension . Ce sera souvent le cas de fournir la même logique de méthode d'extension à des classes ou interfaces similaires, comme par exemple Reversepour des collections génériques et non génériques. Je mettrais toutes les méthodes d'extension pour une seule classe ou interface dans un partiel séparé de la classe statique. Par exemple, j'aurais toutes les méthodes d'extension pour l' IListinterface à un endroit et les mêmes méthodes que IList<T>dans un autre fichier. Une autre approche serait de mettre la même méthode (toutes ses surcharges) dans le même partiel, avec toutes les classes possibles pour le thisparamètre - comme si vous aviez tous lesReverseimplémentations dans un fichier. Cela dépend de celui qui justifierait le mieux la séparation en termes de volume de code ou de conventions internes que vous ou votre organisation respecteriez peut-être.

  • Je n’utilise pas cela, mais j’ai déjà vu des personnes en C / C ++ apprécier l’approche que je vais décrire ici: créez un partial pour la classe avec des méthodes partielles uniquement. Cela ressemble à la méthode C / C ++ pour définir les interfaces et séparer la déclaration de méthode de l'implémentation.

  • Séparation par des préoccupations purement logiques . Si vous travaillez avec une classe volumineuse combinant plusieurs ensembles d'opérations logiques, vous pouvez séparer chaque code lié logiquement en un partiel séparé. L'existence de telles classes va généralement à l'encontre du principe de séparation des préoccupations , mais on observe souvent des cas réels, en particulier pour des bases de code plus anciennes. Un autre utilisateur, Steve Evers, les a mentionnés dans sa réponse à cette question , les appelant dieu objets.. Personnellement, j’utilise l’approche des classes partielles pour scinder le code avant toute refactorisation de fichiers aussi volumineux, afin de faciliter mon travail et de rendre la refactorisation plus transparente. Cela réduira également les conflits que je pourrais éventuellement provoquer lorsque des systèmes de gestion de version tels que SVN sont impliqués.

Ivaylo Slavov
la source
14

Je ne l'ai jamais vu mentionné par quiconque: j'avais l'habitude partialde mettre des classes imbriquées dans leurs propres fichiers.

Tous mes fichiers de code ne contiennent qu'une seule classe, struct, interface ou enum. Il est beaucoup plus facile de trouver la définition d’un objet lorsque les noms de fichiers indiquent le nom de la chose recherchée. Et puisque Visual Studio essaie de faire correspondre les dossiers du projet aux espaces de noms, les noms de fichiers doivent correspondre aux classes.

Cela signifie également qu'une classe NestedClassimbriquée dans MyClassaura propre fichier de dans mes projets: MyClass.NestedClass.cs.

partial class MyClass
{
    private class NestedClass
    {
        // ...
    }
}
Daniel AA Pelsmaeker
la source
1
Je me demande pourquoi on refuserait cette réponse. La pratique mentionnée n’est ni un anti-modèle, ni ne causerait aucun problème à ma connaissance.
Ivaylo Slavov
1
Cela répond également au problème de la taille réduite des fichiers de classe (en termes de lignes de code) tout en permettant une encapsulation correcte du code appartenant à une classe interne.
Redcalx
10

À l'exception de l'utilisation du code généré, je ne les ai jamais vus utilisés pour dissimuler des objets divins . Essayer de comprendre une nouvelle base de code ou de naviguer dans plusieurs fichiers source, qui sont le même objet, est tellement ennuyant.

Donc, quand vous demandez, Why use partial classes?je réponds: sauf si vous utilisez du code généré, ne le faites pas.

Steven Evers
la source
+1 C'est la première fois que je vois ces types d'objets s'appeler un nom (objets divins) et c'est même officiel. Il n'est jamais trop tard pour apprendre quelque chose de nouveau.
Ivaylo Slavov
6

L'organisation du code est la seule raison, mais elle va plus loin qu'il n'y paraît au premier abord. Si vous avez des classes partielles où des pièces sont générées, vous pouvez facilement:

  • Régénérez le code sans avoir à détecter les modifications manuelles dans les classes dans lesquelles vous écrivez (pour ne pas écraser ces parties).
  • Exclure les classes partielles générées de la couverture de test, du contrôle de source, des métriques, etc.
  • Utilisez le code généré sans vous obliger à baser votre stratégie d'héritage sur celui-ci (vous pouvez toujours hériter d'autres classes).
Deckard
la source
1

Il existe également quelques endroits où les classes générées / impliquées sont déclarées partielles. Ainsi, si le développeur doit les étendre, elles ont un accès complet sans avoir à se soucier de l'héritage et de la neutralisation. Examinez les classes générées dans la zone temporaire asp.net après avoir exécuté un site de formulaires Web pour la première fois sous IIS si vous voulez essayer de saisir quelques exemples.

Facture
la source
1

Je peux penser à une utilisation sale pour eux.

Supposons que certaines classes nécessitent des fonctionnalités communes, mais que vous ne souhaitiez pas les intégrer à la chaîne d'héritage située au-dessus d'elles. Ou bien, vous avez un ensemble de classes utilisant une classe d'assistance commune.

Fondamentalement, vous voulez faire un héritage multiple, mais C # ne ressemble pas. Vous utilisez donc partial pour créer ce qui est essentiellement un #include en C / C ++;

Mettez toutes les fonctionnalités dans une classe ou utilisez la classe d'assistance. Copiez ensuite l’aide X fois et renommez-la en classe partielle A, B, C. et mettez partielle sur vos classes A, B, C.

Disclaimer: Oui, c'est mal. Ne le fais jamais - surtout si cela fâche d'autres personnes.

marque
la source
Mixins. Cela pourrait être combiné avec T4 pour éviter la copie manuelle ...
Peter Taylor
Comme indiqué, cela ressemble beaucoup aux mixins. Cependant, vous approchez d'un concept connu sous le nom de "traits" et ils gèreront en toute sécurité ce que vous essayez d'atteindre. J'ai une description indépendante de la langue à publius-ovidius.livejournal.com/314737.html . J'ai cherché une implémentation propre pour C #, mais je n'en ai pas vu.
Ovide