J'utilise la plupart du temps une implémentation d'interface explicite. Voici les principales raisons.
Le refactoring est plus sûr
Lors du changement d'une interface, il est préférable que le compilateur puisse la vérifier. C'est plus difficile avec les implémentations implicites.
Deux cas courants me viennent à l'esprit:
Ajout d'une fonction à une interface, où une classe existante qui implémente cette interface possède déjà une méthode avec la même signature que la nouvelle . Cela peut conduire à un comportement inattendu et m'a mordu dur à plusieurs reprises. Il est difficile de "voir" lors du débogage car cette fonction n'est probablement pas localisée avec les autres méthodes d'interface dans le fichier (le problème d'auto-documentation mentionné ci-dessous).
Suppression d'une fonction d'une interface . Les méthodes implicitement implémentées seront soudainement du code mort, mais les méthodes implémentées explicitement seront prises par une erreur de compilation. Même si le code mort est bon à garder, je veux être obligé de le réviser et de le promouvoir.
Il est regrettable que C # n'ait pas de mot clé qui nous oblige à marquer une méthode comme implémentation implicite, donc le compilateur pourrait faire les vérifications supplémentaires. Les méthodes virtuelles ne présentent aucun des problèmes ci-dessus en raison de l'utilisation obligatoire de «remplacer» et de «nouveau».
Remarque: pour les interfaces fixes ou rarement changeantes (généralement des API du fournisseur), ce n'est pas un problème. Pour mes propres interfaces, cependant, je ne peux pas prédire quand / comment elles changeront.
C'est auto-documenté
Si je vois «public bool Execute ()» dans une classe, cela va demander un travail supplémentaire pour comprendre qu'il fait partie d'une interface. Quelqu'un devra probablement le commenter en le disant, ou le mettre dans un groupe d'autres implémentations d'interface, le tout dans une région ou un commentaire de groupe disant "implémentation de ITask". Bien sûr, cela ne fonctionne que si l'en-tête de groupe n'est pas hors écran.
Alors que: «bool ITask.Execute ()» est clair et sans ambiguïté.
Séparation claire de la mise en œuvre de l'interface
Je pense que les interfaces sont plus «publiques» que les méthodes publiques, car elles sont conçues pour exposer un peu la surface du type de béton. Ils réduisent le type à une capacité, un comportement, un ensemble de traits, etc. Et dans l'implémentation, je pense qu'il est utile de garder cette séparation.
En parcourant le code d'une classe, lorsque je rencontre des implémentations d'interface explicites, mon cerveau passe en mode "contrat de code". Souvent, ces implémentations sont simplement transmises à d'autres méthodes, mais parfois elles feront une vérification supplémentaire de l'état / des paramètres, la conversion des paramètres entrants pour mieux correspondre aux exigences internes, ou même la traduction à des fins de version (c.-à-d. Plusieurs générations d'interfaces, toutes se transformant en implémentations communes).
(Je me rends compte que les publics sont également des contrats de code, mais les interfaces sont beaucoup plus solides, en particulier dans une base de code pilotée par interface où l'utilisation directe de types concrets est généralement un signe de code interne uniquement.)
Connexes: Raison 2 ci-dessus par Jon .
Etc
Plus les avantages déjà mentionnés dans d'autres réponses ici:
Problèmes
Ce n'est pas que du plaisir et du bonheur. Il y a des cas où je m'en tiens aux implicites:
- Types de valeur, car cela nécessitera de la boxe et des performances inférieures. Ce n'est pas une règle stricte et dépend de l'interface et de la façon dont elle est destinée à être utilisée. IComparable? Implicite. IFormable? Probablement explicite.
- Interfaces système triviales qui ont des méthodes qui sont souvent appelées directement (comme IDisposable.Dispose).
En outre, il peut être difficile de faire le casting lorsque vous avez en fait le type concret et que vous souhaitez appeler une méthode d'interface explicite. Je traite cela de deux manières:
- Ajoutez des publics et faites-leur parvenir les méthodes d'interface pour la mise en œuvre. Cela se produit généralement avec des interfaces plus simples lorsque vous travaillez en interne.
- (Ma méthode préférée) Ajoutez un
public IMyInterface I { get { return this; } }
(qui devrait être aligné) et appelez foo.I.InterfaceMethod()
. Si plusieurs interfaces ont besoin de cette capacité, développez le nom au-delà de moi (d'après mon expérience, il est rare que j'aie ce besoin).