Quelles sont les différences dans l'implémentation implicite et explicite des interfaces en C #?
Quand devriez-vous utiliser implicite et quand devriez-vous utiliser explicite?
Y a-t-il des avantages et / ou des inconvénients pour l'un ou l'autre?
Les directives officielles de Microsoft (issues de la première édition des Framework Design Guidelines ) indiquent que l' utilisation d'implémentations explicites n'est pas recommandée , car elle donne au code un comportement inattendu.
Je pense que cette directive est très valable dans un temps pré-IoC , lorsque vous ne faites pas passer les choses sous forme d'interfaces.
Quelqu'un pourrait-il également aborder cet aspect?
Réponses:
Implicite, c'est lorsque vous définissez votre interface via un membre de votre classe. Explicite est lorsque vous définissez des méthodes au sein de votre classe sur l'interface. Je sais que cela semble déroutant, mais voici ce que je veux dire:
IList.CopyTo
serait implicitement implémenté comme:et explicitement comme:
La différence est que l'implémentation implicite vous permet d'accéder à l'interface via la classe que vous avez créée en convertissant l'interface en tant que classe et en tant qu'interface elle-même. L'implémentation explicite vous permet d'accéder à l'interface uniquement en la convertissant en interface elle-même.
J'utilise explicitement principalement pour garder l'implémentation propre, ou lorsque j'ai besoin de deux implémentations. Quoi qu'il en soit, je l'utilise rarement.
Je suis sûr qu'il y a plus de raisons d'utiliser / de ne pas utiliser explicitement que d'autres publieront.
Voir le prochain post dans ce fil pour un excellent raisonnement derrière chacun.
la source
public
mot - clé ... sinon vous obtiendrez une erreurUISwitch IScoreRegPlayerViewCell.markerSwitch { get { return markerSwitch; } }
.IEnumerator<T>.Current
,IEnumerable<T>.GetEnumerator()
etISet<T>.Add(T)
. Ceci est mentionné dans une autre réponse .La définition implicite serait d'ajouter simplement les méthodes / propriétés, etc. demandées par l'interface directement à la classe en tant que méthodes publiques.
Une définition explicite force les membres à être exposés uniquement lorsque vous travaillez directement avec l'interface, et non l'implémentation sous-jacente. Ceci est préférable dans la plupart des cas.
la source
Cat
est en effetIEatable
sans objections.En plus des excellentes réponses déjà fournies, il existe certains cas où une implémentation explicite est REQUISE pour que le compilateur puisse comprendre ce qui est requis. Jetez-y un coup d'œil
IEnumerable<T>
comme un excellent exemple qui reviendra probablement assez souvent.Voici un exemple:
Ici,
IEnumerable<string>
implémenteIEnumerable
, donc nous devons aussi. Mais attendez, les versions générique et normale implémentent toutes deux des fonctions avec la même signature de méthode (C # ignore le type de retour pour cela). C'est tout à fait légal et bien. Comment le compilateur détermine-t-il lequel utiliser? Il vous oblige à n'avoir, au plus, qu'une définition implicite, puis il peut résoudre tout ce dont il a besoin.c'est à dire.
PS: Le petit morceau d'indirection dans la définition explicite pour IEnumerable fonctionne parce qu'à l'intérieur de la fonction, le compilateur sait que le type réel de la variable est une StringList, et c'est ainsi qu'il résout l'appel de fonction. Astucieux petit fait pour implémenter certaines des couches d'abstraction, certaines des interfaces de base .NET semblent s'être accumulées.
la source
abstract
.Pour citer Jeffrey Richter de CLR via C #
( EIMI signifie E xplicit I nterface M ethod I mplementation )
Si vous utilisez une référence d'interface, N'IMPORTE QUELLE chaîne virtuelle peut être explicitement remplacée par EIMI sur n'importe quelle classe dérivée et lorsqu'un objet de ce type est converti en interface, votre chaîne virtuelle est ignorée et l'implémentation explicite est appelée. C'est tout sauf du polymorphisme.
Les EIMI peuvent également être utilisés pour masquer les membres d'interface non fortement typés des implémentations de base d'Interfaces Framework telles que IEnumerable <T> afin que votre classe n'expose pas directement une méthode non fortement typée, mais est syntaxiquement correcte.
la source
Raison n ° 1
J'ai tendance à utiliser une implémentation d'interface explicite lorsque je veux décourager la "programmation vers une implémentation" ( Design Principles from Design Patterns ).
Par exemple, dans une application Web basée sur MVP :
Désormais, une autre classe (telle qu'un présentateur ) est moins susceptible de dépendre de l'implémentation de StandardNavigator et plus susceptible de dépendre de l'interface INavigator (puisque l'implémentation devrait être castée sur une interface pour utiliser la méthode de redirection).
Raison n ° 2
Une autre raison pour laquelle j'opterais pour une implémentation d'interface explicite serait de conserver une interface "par défaut" plus propre pour une classe. Par exemple, si je développais un contrôle serveur ASP.NET , je souhaiterais peut-être deux interfaces:
Un exemple simple suit. Il s'agit d'un contrôle de zone de liste déroulante qui répertorie les clients. Dans cet exemple, le développeur de page Web n'est pas intéressé à remplir la liste; au lieu de cela, ils veulent simplement pouvoir sélectionner un client par GUID ou obtenir le GUID du client sélectionné. Un présentateur remplirait la boîte lors du premier chargement de page, et ce présentateur est encapsulé par le contrôle.
Le présentateur remplit la source de données et le développeur de pages Web n'a jamais besoin d'être conscient de son existence.
Mais ce n'est pas un boulet de canon argenté
Je ne recommanderais pas toujours d'employer des implémentations d'interface explicites. Ce ne sont là que deux exemples où ils pourraient être utiles.
la source
En plus des autres raisons déjà mentionnées, c'est la situation dans laquelle une classe implémente deux interfaces différentes qui ont une propriété / méthode avec le même nom et la même signature.
Ce code se compile et s'exécute correctement, mais la propriété Title est partagée.
De toute évidence, nous voudrions que la valeur de Title retournée dépende du fait que nous traitions Class1 comme un livre ou une personne. C'est à ce moment que nous pouvons utiliser l'interface explicite.
Notez que les définitions d'interface explicites sont supposées être publiques - et donc vous ne pouvez pas les déclarer publiques (ou autrement) explicitement.
Notez également que vous pouvez toujours avoir une version "partagée" (comme indiqué ci-dessus), mais bien que cela soit possible, l'existence d'une telle propriété est discutable. Peut-être pourrait-il être utilisé comme implémentation par défaut de Title - afin que le code existant ne doive pas être modifié pour convertir Class1 en IBook ou IPerson.
Si vous ne définissez pas le titre "implicite" (implicite), les consommateurs de Class1 doivent d' abord transtyper explicitement les instances de Class1 en IBook ou IPerson - sinon le code ne sera pas compilé.
la source
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:
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:
public IMyInterface I { get { return this; } }
(qui devrait être aligné) et appelezfoo.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).la source
Si vous implémentez explicitement, vous ne pourrez référencer les membres de l'interface qu'à travers une référence qui est du type de l'interface. Une référence qui est le type de la classe d'implémentation n'exposera pas ces membres d'interface.
Si votre classe d'implémentation n'est pas publique, à l'exception de la méthode utilisée pour créer la classe (qui pourrait être une fabrique ou un conteneur IoC ), et à l'exception des méthodes d'interface (bien sûr), alors je ne vois aucun avantage à implémenter explicitement interfaces.
Sinon, l'implémentation explicite des interfaces garantit que les références à votre classe d'implémentation concrète ne sont pas utilisées, ce qui vous permet de modifier cette implémentation ultérieurement. "S'assure", je suppose, est "l'avantage". Une implémentation bien factorisée peut accomplir ceci sans implémentation explicite.
L'inconvénient, à mon avis, est que vous vous retrouverez à lancer des types vers / depuis l'interface dans le code d'implémentation qui a accès aux membres non publics.
Comme beaucoup de choses, l'avantage est l'inconvénient (et vice-versa). L'implémentation explicite d'interfaces garantira que votre code d'implémentation de classe concrète n'est pas exposé.
la source
Une implémentation d'interface implicite est l'endroit où vous avez une méthode avec la même signature de l'interface.
Une implémentation d'interface explicite est l'endroit où vous déclarez explicitement à quelle interface la méthode appartient.
MSDN: implémentations d'interfaces implicites et explicites
la source
Chaque membre de classe qui implémente une interface exporte une déclaration qui est sémantiquement similaire à la façon dont les déclarations d'interface VB.NET sont écrites, par exemple
Bien que le nom du membre de la classe corresponde souvent à celui du membre d'interface et que le membre de la classe soit souvent public, aucune de ces choses n'est requise. On peut également déclarer:
Dans ce cas, la classe et ses dérivés seraient autorisés à accéder à un membre de la classe en utilisant le nom
IFoo_Foo
, mais le monde extérieur ne pourrait accéder à ce membre particulier que par transtypageIFoo
. Une telle approche est souvent bonne dans les cas où une méthode d'interface aura un comportement spécifié sur toutes les implémentations, mais un comportement utile sur certaines seulement [par exemple, le comportement spécifié pour laIList<T>.Add
méthode d'une collection en lecture seule est de jeterNotSupportedException
]. Malheureusement, la seule façon appropriée d'implémenter l'interface en C # est:Pas aussi bien.
la source
Une utilisation importante de l'implémentation d'interface explicite est lorsque vous avez besoin d'implémenter des interfaces avec une visibilité mixte .
Le problème et la solution sont bien expliqués dans l'article Interface interne C # .
Par exemple, si vous souhaitez protéger les fuites d'objets entre les couches d'application, cette technique vous permet de spécifier une visibilité différente des membres susceptibles de provoquer la fuite.
la source
Les réponses précédentes expliquent pourquoi la mise en œuvre d' une interface explicitement en C # peut être preferrable (principalement pour des raisons formelles). Cependant, il y a une situation où l'implémentation explicite est obligatoire : afin d'éviter de fuir l'encapsulation lorsque l'interface est non
public
, mais la classe d'implémentation l'estpublic
.La fuite ci-dessus est inévitable car, selon la spécification C # , "Tous les membres de l'interface ont implicitement un accès public". En conséquence, les implémentations implicites doivent également donner
public
accès, même si l'interface elle-même est par exempleinternal
.L'implémentation d'interface implicite en C # est très pratique. En pratique, de nombreux programmeurs l'utilisent tout le temps / partout sans autre considération. Cela conduit au mieux à des surfaces de type en désordre et au pire à une fuite d'encapsulation. D'autres langues, comme F #, ne le permettent même pas .
la source