C # Interfaces. Mise en œuvre implicite et mise en œuvre explicite

quelles sont les différences dans la mise en œuvre des interfaces implicitement et explicitement dans C#?

Quand devez-vous utiliser implicite et quand devez-vous utiliser explicite?

y a-t-il des POUR et/ou des contre à l'un ou l'autre?


lignes directrices officielles de Microsoft (de la première édition lignes directrices sur la conception de cadre ) stipule que en utilisant des implémentations explicites n'est pas recommandé , car il donne le comportement inattendu du code.

je pense que cette ligne directrice est très valide dans un temps pré-CIO , quand vous ne faites pas passer les choses comme des interfaces.

Quelqu'un pourrait-il aborder cet aspect également?

570
demandé sur Guillermo Gutiérrez 2008-09-27 14:56:16

11 réponses

implicite est quand vous définissez votre interface via un membre sur votre classe. Explicit est quand vous définissez des méthodes dans votre classe sur l'interface. Je sais que cela semble déroutant, mais voici ce que je veux dire: IList.CopyTo serait implicitement mis en œuvre comme:

public void CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

et explicitement comme:

void ICollection.CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

la différence étant que implicitement est accessible à travers votre classe que vous avez créé quand il est moulé comme cette classe aussi bien que quand il est moulé comme l'interface. La mise en œuvre explicite permet de n'être accessible que lorsqu'il s'agit de l'interface elle-même.

MyClass myClass = new MyClass(); // Declared as concrete class
myclass.CopyTo //invalid with explicit
((IList)myClass).CopyTo //valid with explicit.

j'utilise explicit principalement pour garder l'implémentation propre, ou quand j'ai besoin de deux implémentations. Mais peu importe j'ai rarement l'utiliser.

je suis sûr qu'il y a plus de raisons pour l'utiliser/ne pas utiliser que les autres post.

voir la next post dans ce fil pour excellent raisonnement derrière chacun.

447
répondu mattlant 2017-05-23 12:26:32

définition implicite serait d'ajouter simplement les méthodes / propriétés,etc. exigé par l'interface directement à la classe des méthodes publiques.

définition explicite oblige les membres à être exposés que lorsque vous travaillez avec l'interface directement, et non l'implémentation sous-jacente. C'est préférable dans la plupart des cas.

  1. en travaillant directement avec l'interface, vous ne reconnaissez pas, et de couplage de votre code à l' implémentation sous-jacente.
  2. dans le cas où vous avez déjà, disons, un nom de bien public dans votre code et vous voulez mettre en œuvre une interface qui a également un Nommer la propriété, en le faisant explicitement gardera les deux séparés. Même s'ils faisaient la même chose, je serais encore délégué de l'explicite appel à la propriété Name. On ne sait jamais, on peut vouloir changer comment le nom fonctionne pour la classe normale et comment le nom, l'interface la propriété fonctionne plus tard.
  3. si vous implémentez une interface implicitement alors votre classe expose maintenant de nouveaux comportements qui pourraient n'être pertinents que pour un client du interface et cela signifie que vous ne gardez pas vos classes succinctes assez (à mon avis).
180
répondu Phil Bennett 2017-12-04 18:32:55

en plus des excellentes réponses déjà fournies, il y a des cas où une implémentation explicite est nécessaire pour que le compilateur puisse comprendre ce qui est requis. Jetez un oeil à IEnumerable<T> comme un premier exemple qui sera probablement assez souvent.

voici un exemple:

public abstract class StringList : IEnumerable<string>
{
    private string[] _list = new string[] {"foo", "bar", "baz"};

    // ...

    #region IEnumerable<string> Members
    public IEnumerator<string> GetEnumerator()
    {
        foreach (string s in _list)
        { yield return s; }
    }
    #endregion

    #region IEnumerable Members
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
    #endregion
}

ici, IEnumerable<string> met en œuvre IEnumerable , donc nous devons aussi. Mais attendez, à la fois la version générique et la version normale les deux implémentent des fonctions avec la même méthode de signature (C# ignore le type de retour pour cela). C'est tout à fait légal et bien. Comment le compilateur résoudre à utiliser? Cela vous oblige à n'avoir, au plus, qu'une seule définition implicite, puis il peut résoudre tout ce dont il a besoin.

ie.

StringList sl = new StringList();

// uses the implicit definition.
IEnumerator<string> enumerableString = sl.GetEnumerator();
// same as above, only a little more explicit.
IEnumerator<string> enumerableString2 = ((IEnumerable<string>)sl).GetEnumerator();
// returns the same as above, but via the explicit definition
IEnumerator enumerableStuff = ((IEnumerable)sl).GetEnumerator();

PS: le petit morceau d'indirecte dans la définition explicite pour IEnumerable works parce qu'à l'intérieur de la fonction le compilateur sait que le type réel de la variable est une chaîne de caractères, et c'est comme ça qu'elle résout l'appel de fonction. Très peu de faits pour mettre en œuvre certaines couches d'abstraction certaines des interfaces.net semblent s'être accumulées.

63
répondu Matthew Scharley 2014-07-07 11:09:44

raison # 1

j'ai tendance à utiliser l'implémentation d'interface explicite quand je veux décourager" la programmation À une implémentation "( les principes de conception des modèles de conception ).

par exemple, dans une application web MVP basée sur:

public interface INavigator {
    void Redirect(string url);
}

public sealed class StandardNavigator : INavigator {
    void INavigator.Redirect(string url) {
        Response.Redirect(url);
    }
}

maintenant une autre classe (comme un présentateur ) est moins susceptible de dépendre de la Mise en œuvre StandardNavigator et plus susceptible de dépendre de l'interface INavigator (puisque la mise en œuvre devrait être coulée dans une interface pour faire usage de la méthode de redirection).

Raison #2

une autre raison pour laquelle je pourrais aller avec une implémentation d'interface explicite serait de garder l'interface" par défaut " d'une classe plus propre. Par exemple, si je développais un ASP.NET contrôle du serveur, je pourrais vouloir deux interfaces:

  1. l'interface principale de la classe, qui est utilisée par les développeurs de pages web; et
  2. A "caché" de l'interface utilisée par le présentateur que je développe pour gérer le contrôle de la logique du

un exemple simple suit. C'est un contrôle de boîte à boutons qui liste les clients. Dans cet exemple, le développeur de page web n'est pas intéressé à peupler la liste; au lieu de cela, ils veulent juste être en mesure de sélectionner un client par GUID ou à obtenir le client sélectionné est GUID. Un présentateur remplirait la boîte à la première page, et ce présentateur est encapsulé par le contrôle.

public sealed class CustomerComboBox : ComboBox, ICustomerComboBox {
    private readonly CustomerComboBoxPresenter presenter;

    public CustomerComboBox() {
        presenter = new CustomerComboBoxPresenter(this);
    }

    protected override void OnLoad() {
        if (!Page.IsPostBack) presenter.HandleFirstLoad();
    }

    // Primary interface used by web page developers
    public Guid ClientId {
        get { return new Guid(SelectedItem.Value); }
        set { SelectedItem.Value = value.ToString(); }
    }

    // "Hidden" interface used by presenter
    IEnumerable<CustomerDto> ICustomerComboBox.DataSource { set; }
}

le présentateur popule la source de données, et le développeur de page web n'a jamais besoin d'être au courant de son existence.

Mais Ce n'est Pas un Argent Boulet de canon

Je ne recommande pas toujours d'utiliser des implémentations d'interface explicites. Ce sont juste deux exemples où ils pourraient être utiles.

29
répondu Jon Nadal 2016-05-23 17:17:00

pour citer Jeffrey Richter de CLR via C#

( EIMI signifie E xplicit je nterface M méthode je la mise en place)

il est très important pour vous de comprendre certaines ramifications qui existe lors de l'utilisation D'EIMIs. Et à cause de ces ramifications, vous devriez essayer de évitez EIMIs autant que possible. Heureusement, les interfaces génériques aident vous évitez EIMIs un peu. Mais il n'y peut-être encore des moments où vous aurez besoin de pour les utiliser (comme la mise en place de deux les méthodes d'interface avec le même nom et la signature). Ici, ce sont les grandes problèmes avec EIMIs:

  • il n'y a pas de documentation expliquant comment un type spécifiquement met en œuvre une méthode EIMI, et là N'est pas Microsoft Visual Studio IntelliSense support.
  • Les instances de type
  • sont encadrées lorsqu'elles sont envoyées à une interface.
  • un EIMI ne peut pas être appelé par un type dérivé.

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 moulé à l'interface, votre chaîne virtuelle est ignorée et l'implémentation explicite est appelée. Qui n'a rien de polymorphisme.

EIMIs peut également être utilisé pour cacher les membres d'interface non fortement typés des implémentations d'Interfaces de Framework de base comme IEnumerable de sorte que votre classe n'expose pas une méthode non fortement typée directement, mais est syntaxique correcte.

29
répondu Valentin Kuzub 2016-05-23 17:22:41

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.

/// <summary>
/// This is a Book
/// </summary>
interface IBook
{
    string Title { get; }
    string ISBN { get; }
}

/// <summary>
/// This is a Person
/// </summary>
interface IPerson
{
    string Title { get; }
    string Forename { get; }
    string Surname { get; }
}

/// <summary>
/// This is some freaky book-person.
/// </summary>
class Class1 : IBook, IPerson
{
    /// <summary>
    /// This method is shared by both Book and Person
    /// </summary>
    public string Title
    {
        get
        {
            string personTitle = "Mr";
            string bookTitle = "The Hitchhikers Guide to the Galaxy";

            // What do we do here?
            return null;
        }
    }

    #region IPerson Members

    public string Forename
    {
        get { return "Lee"; }
    }

    public string Surname
    {
        get { return "Oades"; }
    }

    #endregion

    #region IBook Members

    public string ISBN
    {
        get { return "1-904048-46-3"; }
    }

    #endregion
}

ce code compile et exécute OK, mais la propriété Title est partagée.

de toute évidence, nous aimerions que la valeur du titre revienne en fonction du fait que nous traitions classe1 comme un livre ou une personne. C'est alors que nous pouvons utiliser explicitement l' interface.

string IBook.Title
{
    get
    {
        return "The Hitchhikers Guide to the Galaxy";
    }
}

string IPerson.Title
{
    get
    {
        return "Mr";
    }
}

public string Title
{
    get { return "Still shared"; }
}

notez que les définitions explicites de l'interface sont supposées être publiques - et donc vous ne pouvez pas les déclarer être publiques (ou autrement) explicitement.

Notez aussi que vous pouvez toujours avoir une version "partagée" (comme montré ci-dessus), mais bien que cela soit possible, l'existence d'une telle propriété est discutable. Peut - être pourrait-il être utilisé comme une mise en œuvre par défaut du titre-de sorte que le code existant n'aurait pas à être modifié pour casting Class1 à IBook ou IPerson.

si vous ne définissez pas le titre" partagé "(implicite), les consommateurs de classe1 doivent lancer explicitement les instances de classe1 à IBook ou à IPerson en premier - sinon le code ne sera pas compilé.

17
répondu Lee Oades 2016-05-23 17:12:57

j'utilise l'implémentation d'interface explicite la plupart du temps. Voici les principales raisons.

Refactoring est plus sûr

lors de la modification d'une interface, il est préférable que le compilateur puisse la vérifier. C'est plus difficile avec les implémentations.

deux cas courants viennent à l'esprit:

  • ajouter une fonction à une interface, où un la classe qui implémente cette interface a déjà une méthode avec la même signature que la nouvelle . Cela peut conduire à un comportement inattendu, et m'a mordu plusieurs fois. Il est difficile de "voir" lors du débogage parce que cette fonction n'est probablement pas situé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 explicitement implémentées seront attrapées par une erreur de compilation. Même si le code mort est bon à garder, je veux être forcé 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 une implémentation implicite, de sorte que le compilateur pourrait faire les vérifications supplémentaires. Les méthodes virtuelles n'ont aucun des problèmes ci-dessus en raison de nécessaire l'utilisation de "remplacement" et les "nouveaux".

Note: pour les interfaces fixes ou qui changent rarement (typiquement à partir des API fournisseurs), ce n'est pas un problème. Pour mes propres interfaces, cependant, je ne peux pas prédire quand/comment ils changeront.

, C'est l'auto-documentation

si je vois 'public bool Execute()' dans une classe, ça va prendre du travail supplémentaire pour comprendre que ça fait partie d'une interface. Quelqu'un aurez probablement à commentez-le en disant cela, ou mettez-le dans un groupe d'autres implémentations d'interfaces, toutes sous une région ou un groupe de commentaires disant "implémentation D'ITask". Bien sûr, cela ne fonctionne que si l'en-tête du groupe n'est pas à l'écran..

attendu que: "bool ITask.Execute()' est clair et sans ambiguïté.

séparation Claire de l'implémentation de l'interface

je pense que les interfaces sont plus "publiques" que les méthodes publiques parce qu'ils sont conçus pour exposer juste un peu de la surface du type béton. Ils réduisent le type à une capacité, un comportement, un ensemble de traits, etc. Et dans la mise en œuvre, je pense qu'il est utile de garder cette séparation.

comme je regarde à travers le code d'une classe, quand je tombe sur des implémentations d'interface explicites, mon cerveau se déplace en mode" contrat de code". Souvent, ces implémentations sont simplement redirigées vers d'autres méthodes, mais parfois elles vont faire des vérification de l'état/param, conversion des paramètres entrants pour mieux correspondre aux exigences internes, ou même la traduction à des fins de versioning (c.-à-d. plusieurs générations d'interfaces toutes pointant vers le bas à des implémentations communes).

(je me rends compte que les publics sont aussi des contrats de code, mais les interfaces sont beaucoup plus solides, surtout dans une base de codes d'interface où l'utilisation directe de types concrets est habituellement un signe de code interne.)

Related: Raison 2 ci-dessus par Jon .

et ainsi de suite

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 implicits:

  • types de valeurs, parce que cela nécessitera de la boxe et un taux de perf inférieur. Ce n'est pas une règle stricte, et dépend de l'interface et la façon dont il est destiné à être utilisé. IComparable? Implicite. IFormattable? Probablement explicite.
  • interfaces de système Trivial qui ont des méthodes qui sont souvent appelé directement (comme IDisposable.Disposer.)

aussi, il peut être pénible de faire le moulage quand vous avez en fait le type de béton et que vous voulez appeler une méthode d'interface explicite. Je m'y prends de deux façons:

  1. ajouter publics et ont les méthodes d'interface leur transmettre pour la mise en œuvre. Cela se produit généralement avec des interfaces plus simples lorsque vous travaillez en interne.
  2. (mon préféré méthode) Ajouter un public IMyInterface I { get { return this; } } (qui devrait être en ligne) et appeler foo.I.InterfaceMethod() . Si plusieurs interfaces ont besoin de cette capacité, étendez le nom au-delà de I (d'après mon expérience, il est rare que j'ai ce besoin).
11
répondu scobi 2017-05-23 12:34:41

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 de l'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 usine ou IOC conteneur), et à l'exception des méthodes d'interface (bien sûr), alors I ne voyez aucun avantage à explicitement implémenter des interfaces.

sinon, implémenter explicitement les interfaces garantit que les références à votre classe d'implémentation concrète ne sont pas utilisées, vous permettant de modifier cette implémentation à un moment ultérieur. "S'assure", je suppose, est l ' "avantage". Une mise en œuvre bien équilibrée peut y parvenir sans mise en œuvre explicite.

l'inconvénient, à mon avis, est que vous vous trouverez la distribution des types vers / depuis l'interface dans le code de mise en œuvre qui a accès à des non-membres du public.

comme beaucoup de choses, l'avantage est le désavantage (et vice-versa). L'implémentation explicite des interfaces garantit que votre code d'implémentation de classe concret n'est pas exposé.

8
répondu Bill 2016-07-17 18:28:25

une implémentation d'interface implicite est 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.

interface I1
{
    void implicitExample();
}

interface I2
{
    void explicitExample();
}


class C : I1, I2
{
    void implicitExample()
    {
        Console.WriteLine("I1.implicitExample()");
    }


    void I2.explicitExample()
    {
        Console.WriteLine("I2.explicitExample()");
    }
}

MSDN: implementations d'interfaces implicites et explicites

6
répondu Yochai Timmer 2011-05-26 14:12:52

chaque membre de classe qui met en œuvre une interface exporte une déclaration qui est sémantiquement similaire à la manière VB.NET les déclarations d'interface sont écrites, par exemple

Public Overridable Function Foo() As Integer Implements IFoo.Foo

bien que le nom du membre de la classe corresponde souvent à celui du membre de l'interface, et que le membre de la classe soit souvent public, aucun de ces éléments n'est requis. On peut aussi déclarer:

Protected Overridable Function IFoo_Foo() As Integer Implements IFoo.Foo

, auquel cas la catégorie et ses dérivés seraient autorisé à accéder à un membre de classe en utilisant le nom IFoo_Foo , mais le monde extérieur ne serait en mesure d'accéder à ce membre particulier en moulant à IFoo . 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 utile sur certains seulement [par exemple, le comportement spécifié pour la méthode IList<T>.Add d'une collection en lecture seule est de jeter NotSupportedException ]. Malheureusement, la seule façon correcte d'implémenter l'interface dans C# est:

int IFoo.Foo() { return IFoo_Foo(); }
protected virtual int IFoo_Foo() { ... real code goes here ... }

pas aussi sympa.

5
répondu supercat 2016-05-23 17:29:12

L'une des utilisations importantes de l'implémentation explicite de l'interface est lorsqu'il faut implémenter des interfaces avec mixed visibility .

le problème et la solution sont bien expliqués dans l'article C# Internal Interface .

par exemple, si vous voulez protéger la fuite d'objets entre les couches d'application, cette technique vous permet de spécifier une visibilité différente des membres ça pourrait causer la fuite.

1
répondu nrodic 2016-05-23 17:27:47