Alors, quel est exactement un bon cas d'utilisation pour implémenter une interface explicitement?
Est-ce uniquement pour que les utilisateurs de la classe n'aient pas à regarder toutes ces méthodes / propriétés dans Intellisense?
Si vous implémentez deux interfaces, toutes deux avec la même méthode et des implémentations différentes, vous devez implémenter explicitement.
public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}
void IDoItSlow.Go()
{
}
}
Il est utile de masquer le membre non préféré. Par exemple, si vous implémentez les deux
IComparable<T>
etIComparable
qu'il est généralement plus agréable de masquer laIComparable
surcharge pour ne pas donner aux gens l'impression que vous pouvez comparer des objets de types différents. De même, certaines interfaces ne sont pas compatibles CLS, par exempleIConvertible
, si vous n'implémentez pas explicitement l'interface, les utilisateurs finaux de langages qui nécessitent la conformité CLS ne peuvent pas utiliser votre objet. (Ce qui serait très désastreux si les implémenteurs BCL ne cachaient pas les membres IConvertible des primitives :))Une autre note intéressante est que normalement l'utilisation d'une telle construction signifie que la structure qui implémente explicitement une interface ne peut les invoquer qu'en encadrant le type d'interface. Vous pouvez contourner cela en utilisant des contraintes génériques:
Ne mettra pas en boîte un int lorsque vous lui en passerez un.
la source
string
était là avant les génériques et cette pratique était en vogue. Lorsque .net 2 est arrivé, ils ne voulaient pas casser l'interface publique du,string
alors ils l'ont laissé tel quel avec la sauvegarde en place.Quelques raisons supplémentaires pour implémenter une interface explicitement:
rétrocompatibilité : en cas de
ICloneable
modification de l' interface, les membres de la classe de méthode d'implémentation n'ont pas à changer leurs signatures de méthode.code plus propre : il y aura une erreur du compilateur si la
Clone
méthode est supprimée de ICloneable, cependant si vous implémentez la méthode implicitement, vous pouvez vous retrouver avec des méthodes publiques `` orphelines '' inutiliséestypage fort : pour illustrer l'histoire de supercat avec un exemple, ce serait mon exemple de code préféré, l'implémentation
ICloneable
permet explicitementClone()
d'être fortement typé lorsque vous l'appelez directement en tant queMyObject
membre d'instance:la source
interface ICloneable<out T> { T Clone(); T self {get;} }
. Notez qu'il n'y a délibérément aucuneICloneable<T>
contrainte sur T. Alors qu'un objet ne peut généralement être cloné en toute sécurité que si sa base le peut, on peut souhaiter dériver d'une classe de base qui pourrait être clonée en toute sécurité un objet d'une classe qui ne le peut pas. Pour permettre cela, je recommande de ne pas laisser les classes héritables exposer une méthode de clone public. Au lieu de cela, ont des classes héritables avec uneprotected
méthode de clonage et des classes scellées qui en dérivent et exposent le clonage public.Une autre technique utile consiste à demander à l'implémentation publique d'une fonction d'une méthode de renvoyer une valeur plus spécifique que celle spécifiée dans une interface.
Par exemple, un objet peut implémenter
ICloneable
, mais saClone
méthode visible publiquement renvoie son propre type.De même, an
IAutomobileFactory
peut avoir uneManufacture
méthode qui retourne anAutomobile
, mais aFordExplorerFactory
, qui implémenteIAutomobileFactory
, peut avoir saManufacture
méthode retourner aFordExplorer
(qui dérive deAutomobile
). Le code qui sait qu'il a unFordExplorerFactory
pourrait utiliser desFordExplorer
propriétés spécifiques sur un objet retourné par unFordExplorerFactory
sans avoir à faire de transtypage, tandis que le code qui savait simplement qu'il avait un type deIAutomobileFactory
traiterait simplement son retour comme unAutomobile
.la source
C'est également utile lorsque vous avez deux interfaces avec le même nom de membre et la même signature, mais que vous souhaitez en modifier le comportement en fonction de son utilisation. (Je ne recommande pas d'écrire un code comme celui-ci):
la source
public class Animal : Cat, Dog
Il peut garder l'interface publique plus propre pour implémenter explicitement une interface, c'est-à-dire que votre
File
classe peut implémenterIDisposable
explicitement et fournir une méthode publiqueClose()
qui pourrait avoir plus de sens pour un consommateur queDispose(
).F # propose uniquement une implémentation d'interface explicite, vous devez donc toujours effectuer un cast vers l'interface particulière pour accéder à ses fonctionnalités, ce qui permet une utilisation très explicite (sans jeu de mots) de l'interface.
la source
Dispose
sont celles qui ne nécessiteront jamais de nettoyage); un meilleur exemple serait quelque chose comme l'implémentation d'une collection immuable deIList<T>.Add
.Si vous avez une interface interne et que vous ne souhaitez pas implémenter publiquement les membres de votre classe, vous les implémenterez explicitement. Les implémentations implicites doivent être publiques.
la source
Une autre raison de l'implémentation explicite est la maintenabilité .
Quand une classe est "occupée" - oui, cela arrive, nous n'avons pas tous le luxe de refactoriser le code des autres membres de l'équipe - alors avoir une implémentation explicite indique clairement qu'une méthode est là pour satisfaire un contrat d'interface.
Cela améliore donc la "lisibilité" du code.
la source
#region
pour cela, avec une chaîne de titre appropriée. Et un commentaire sur la méthode.Un autre exemple est donné par
System.Collections.Immutable
, dans lequel les auteurs ont choisi d'utiliser la technique pour conserver une API familière pour les types de collection tout en supprimant les parties de l'interface qui n'ont aucune signification pour leurs nouveaux types.Concrètement,
ImmutableList<T>
implémenteIList<T>
et doncICollection<T>
( afin de permettreImmutableList<T>
d'être utilisé plus facilement avec du code hérité), n'a pourtantvoid ICollection<T>.Add(T item)
aucun sens pour unImmutableList<T>
: puisque l'ajout d'un élément à une liste immuable ne doit pas changer la liste existante,ImmutableList<T>
dérive également deIImmutableList<T>
dontIImmutableList<T> Add(T item)
peut être utilisé pour listes immuables.Ainsi, dans le cas de
Add
, les implémentationsImmutableList<T>
finissent par se présenter comme suit:la source
Dans le cas d'interfaces explicitement définies, toutes les méthodes sont automatiquement privées, vous ne pouvez pas leur donner un modificateur d'accès public. Supposer:
la source
Voici comment nous pouvons créer une interface explicite: si nous avons 2 interfaces et que les deux interfaces ont la même méthode et qu'une seule classe hérite de ces 2 interfaces, donc lorsque nous appelons une méthode d'interface, le compilateur a confondu la méthode à appeler, nous pouvons donc gérer ce problème à l'aide de l'interface explicite. Voici un exemple que j'ai donné ci-dessous.
la source