Pourquoi implémenter l’interface explicitement?

Alors, quel est exactement le cas d’utilisation de l’implémentation d’une interface?

Est-ce seulement pour que les personnes utilisant la classe n’aient pas à regarder toutes ces méthodes / propriétés dans intellisense?

Si vous implémentez deux interfaces, à la fois avec la même méthode et avec 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 cacher le membre non préféré. Par exemple, si vous implémentez à la fois IComparable et IComparable il est généralement IComparable de masquer la surcharge IComparable pour ne pas donner l’impression que vous pouvez comparer des objects de types différents. De même, certaines interfaces ne sont pas compatibles avec CLS, comme IConvertible . Par conséquent, si vous IConvertible pas explicitement l’interface, les utilisateurs finaux de langages nécessitant la conformité CLS ne peuvent pas utiliser votre object. (Ce qui serait très désastreux si les implémenteurs de la BCL ne cachaient pas les membres convertibles des primitives :))

Une autre note intéressante est que normalement, utiliser une telle construction signifie que la structure qui implémente explicitement une interface ne peut les appeler qu’en insérant une interface dans le type d’interface. Vous pouvez contourner ce problème en utilisant des contraintes génériques:

 void SomeMethod(T obj) where T:IConvertible 

Ne cochez pas un int lorsque vous lui en transmettez un.

Quelques raisons supplémentaires pour implémenter une interface explicitement:

Compatibilité ascendante : ICloneable interface ICloneable change, les membres de la classe d’implémentation de la méthode n’ont pas à modifier leurs signatures de méthode.

code de nettoyage : il y aura une erreur de compilation si la méthode Clone est supprimée d’ICloneable, cependant si vous implémentez implicitement la méthode, vous pouvez vous retrouver avec des méthodes publiques «orphelines» inutilisées

typage fort : pour illustrer l’histoire de supercat avec un exemple, ce serait mon exemple de code préféré, l’implémentation d’ ICloneable permet explicitement à Clone() d’être fortement typé lorsque vous l’appelez directement en tant que membre d’instance MyObject :

 public class MyObject : ICloneable { public MyObject Clone() { // my cloning logic; } object ICloneable.Clone() { return this.Clone(); } } 

Une autre technique utile consiste à faire en sorte que l’implémentation publique d’une méthode par une fonction retourne une valeur plus spécifique que celle spécifiée dans une interface.

Par exemple, un object peut implémenter ICloneable, mais sa méthode Clone publiquement visible renvoie toujours son propre type.

De même, une IAutomobileFactory peut avoir une méthode de fabrication qui renvoie une Automobile, mais une FordExplorerFactory, qui implémente IAutomobileFactory, peut avoir sa méthode de fabrication pour retourner une FordExplorer (dérivée d’Automobile). Le code qui sait qu’il a une FordExplorerFactory pourrait utiliser des propriétés spécifiques à FordExplorer sur un object renvoyé par une FordExplorerFactory sans avoir à transtyper, alors que le code qui savait simplement qu’il possédait un certain type IAutomobileFactory traiterait simplement son retour en tant qu’Automobile.

Il 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 changer le comportement en fonction de son utilisation. (Je ne recommande pas d’écrire du code comme celui-ci):

 interface Cat { ssortingng Name {get;} } interface Dog { ssortingng Name{get;} } public class Animal : Cat, Dog { ssortingng Cat.Name { get { return "Cat"; } } ssortingng Dog.Name { get { return "Dog"; } } } static void Main(ssortingng[] args) { Animal animal = new Animal(); Cat cat = animal; //Note the use of the same instance of Animal. All we are doing is picking which interface implementation we want to use. Dog dog = animal; Console.WriteLine(cat.Name); //Prints Cat Console.WriteLine(dog.Name); //Prints Dog } 

Si vous avez une interface interne et que vous ne souhaitez pas implémenter les membres de votre classe publiquement, vous devez les implémenter explicitement. Les implémentations implicites doivent être publiques.

Il peut empêcher le nettoyeur d’interface publique d’implémenter explicitement une interface, c.-à-d. File votre classe File peut implémenter explicitement IDisposable et fournir une méthode publique Close() qui pourrait avoir plus de sens pour un consommateur que Dispose( ).

F # ne propose qu’une implémentation d’interface explicite. Vous devez donc toujours utiliser l’interface spécifique pour accéder à ses fonctionnalités, ce qui rend l’utilisation de l’interface très explicite (sans jeu de mots).

Une autre raison de la mise en œuvre explicite est la maintenabilité .

Lorsqu’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 l’implémentation explicite montre clairement qu’une méthode est en place pour satisfaire un contrat d’interface.

Cela améliore donc la “lisibilité” du code.

Dans le cas d’interfaces explicitement définies, toutes les méthodes sont automatiquement privées, vous ne pouvez pas leur donner de modificateur d’access public. Supposer:

 interface Iphone{ void Money(); } interface Ipen{ void Price(); } class Demo : Iphone, Ipen{ void Iphone.Money(){ //it is private you can't give public Console.WriteLine("You have no money"); } void Ipen.Price(){ //it is private you can't give public Console.WriteLine("You have to paid 3$"); } } // So you have to cast to call the method class Program { static void Main(ssortingng[] args) { Demo d = new Demo(); Iphone i1 = (Iphone)d; i1.Money(); ((Ipen)i1).Price(); Console.ReadKey(); } } // You can't call methods by direct class object 

Voici comment nous pouvons créer une interface explicite: Si nous avons 2 interfaces et que l’interface a la même méthode et qu’une seule classe hérite de ces 2 interfaces, lorsque nous appelons une méthode d’interface, le compilateur a confondu la méthode à appeler. gérer ce problème en utilisant l’interface explicite. Voici un exemple que j’ai donné ci-dessous.

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace oops3 { interface I5 { void getdata(); } interface I6 { void getdata(); } class MyClass:I5,I6 { void I5.getdata() { Console.WriteLine("I5 getdata called"); } void I6.getdata() { Console.WriteLine("I6 getdata called"); } static void Main(ssortingng[] args) { MyClass obj = new MyClass(); ((I5)obj).getdata(); Console.ReadLine(); } } } 

Un exemple différent 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 éliminant les parties de l’interface qui ne signifient rien pour leurs nouveaux types.

Concrètement, ImmutableList implémente IList et donc ICollection (pour permettre à ImmutableList d’être utilisé plus facilement avec le code hérité), alors que void ICollection.Add(T item) ne rend pas sens pour une ImmutableList : puisque l’ajout d’un élément à une liste immuable ne doit pas modifier la liste existante, ImmutableList dérive également de IImmutableList dont la liste IImmutableList Add(T item) peut être utilisée .

Ainsi, dans le cas de Add , les implémentations dans ImmutableList finissent par ressembler à ceci:

 public ImmutableList Add(T item) { // Create a new list with the added item } IImmutableList IImmutableList.Add(T value) => this.Add(value); void ICollection.Add(T item) => throw new NotSupportedException(); int IList.Add(object value) => throw new NotSupportedException();