web-dev-qa-db-fra.com

Pourquoi implémenter l'interface explicitement?

Alors, quel est exactement un bon cas d'utilisation pour implémenter explicitement une interface? 

Est-ce seulement pour que les utilisateurs de la classe n'aient pas à examiner toutes ces méthodes/propriétés dans intellisense?

116
Master Morality

Si vous implémentez deux interfaces, 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()
    {
    }
}
138
Iain

Il est utile de masquer le membre non préféré. Par exemple, si vous implémentez à la fois IComparable<T> et IComparable, il est généralement préférable de masquer la surcharge IComparable 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 conformes à CLS, comme IConvertible. Par conséquent, si vous ne l'implémentez pas explicitement, les utilisateurs finaux des langues nécessitant la conformité CLS ne peuvent pas utiliser votre objet. (Ce qui serait très désastreux si les responsables de la mise en œuvre de la BCL ne cachaient pas les membres IConvertible des primitives :))

Une autre remarque 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 que par boxing au type d'interface. Vous pouvez contourner cela en utilisant des contraintes génériques:

void SomeMethod<T>(T obj) where T:IConvertible

Ne boxez pas un int lorsque vous lui en passez un.

63
Michael B

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

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

code de nettoyage : il y aura une erreur de compilation si la méthode Clone est supprimée de ICloneable. Toutefois, si vous implémentez implicitement la méthode, vous pouvez vous retrouver avec des méthodes publiques "orphelines" non utilisées.

strong typing : Pour illustrer l'histoire de supercat par un exemple, il s'agirait de l'exemple de code que je préfère, implémenter ICloneable permet explicitement à Clone() d'être fortement typé lorsque vous l'appelez directement en tant que membre d'une instance MyObject:

public class MyObject : ICloneable
{
  public MyObject Clone()
  {
    // my cloning logic;  
  }

  object ICloneable.Clone()
  {
    return this.Clone();
  }
}
36
Wiebe Tijsma

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

Par exemple, un objet peut implémenter ICloneable, mais sa méthode Clone visible par le public doit toujours retourner son propre type. 

De même, un IAutomobileFactory peut avoir une méthode Manufacture qui retourne un Automobile, mais un FordExplorerFactory, qui implémente IAutomobileFactory, pourrait avoir sa méthode Manufacture renvoyer un FordExplorer (dérivé de Automobile). Un code qui sait qu'il a FordExplorerFactory pourrait utiliser FordExplorer-spécifique des propriétés d'un objet retourné par un FordExplorerFactory sans avoir à transtyper, alors qu'un code qui savait simplement qu'il avait un type de IAutomobileFactory traiterait simplement de son retour comme Automobile.

12
supercat

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 l'utilisation. (Je ne recommande pas d'écrire un code comme celui-ci):

interface Cat
{
    string Name {get;}
}

interface Dog
{
    string Name{get;}
}

public class Animal : Cat, Dog
{
    string Cat.Name
    {
        get
        {
            return "Cat";
        }
    }

    string Dog.Name
    {
        get
        {
            return "Dog";
        }
    }
}
static void Main(string[] 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
}
7
vcsjones

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.

5
Mike Dour

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

F # only offre une implémentation explicite d’interface. Vous devez donc toujours utiliser l’interface particulière pour accéder à ses fonctionnalités, ce qui en fait une utilisation très explicite (sans jeu de mots) de l’interface.

5
BrokenGlass

Une autre raison de la mise en œuvre explicite est pour 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, 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.

4
h bob

Un exemple différent est donné par System.Collections.Immutable, dans lequel les auteurs ont choisi d'utiliser cette technique pour conserver une API familière pour les types de collection tout en grattant les parties de l'interface qui n'ont aucune signification pour leurs nouveaux types.

Concrètement, ImmutableList<T> implémente IList<T> et donc ICollection<T> ( afin de permettre à ImmutableList<T> d'être utilisé plus facilement avec du code hérité), pourtant void ICollection<T>.Add(T item) n'a aucun sens pour un ImmutableList<T>: puisque l'ajout d'un élément à une liste immuable doit ne change pas la liste existante, ImmutableList<T> dérive également de IImmutableList<T> dont IImmutableList<T> Add(T item) peut être utilisé pour des listes immuables.

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

public ImmutableList<T> Add(T item)
{
    // Create a new list with the added item
}

IImmutableList<T> IImmutableList<T>.Add(T value) => this.Add(value);

void ICollection<T>.Add(T item) => throw new NotSupportedException();

int IList.Add(object value) => throw new NotSupportedException();
1
fuglede

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'accès 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(string[] 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
0
Md Shahriar

Voici comment nous pouvons créer une interface explicite: Si nous avons 2 interfaces et que l'interface possède la même méthode et qu'une seule classe hérite de ces 2 interfaces, le compilateur a confondu la méthode lorsque nous appelons une méthode d'interface. pour être appelé, afin que nous puissions gérer ce problème à l’aide de Explicit Interface . 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(string[] args)
        {
            MyClass obj = new MyClass();
            ((I5)obj).getdata();                     

            Console.ReadLine();    
        }
    }
}
0
Debendra Dash