Interfaces C #. Implémentation implicite par rapport à implémentation explicite

632

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?

Seb Nilsson
la source
Lire l'article complet sur les interfaces C #: planetofcoders.com/c-interfaces
Gaurav Agrawal
Oui, les interfaces explicites doivent être évités et une approche plus professionnelle plût à mettre en œuvre ISP (Interface principe de ségrégation) est ici un article détaillé sur le même codeproject.com/Articles/1000374/...
Shivprasad Koirala

Réponses:

492

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.CopyToserait implicitement implémenté 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 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.

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

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.

mattlant
la source
8
Je sais que ce post est ancien mais je l'ai trouvé très utile - une chose à noter si ce n'est pas clair parce que ce n'était pas pour moi, c'est que dans l'exemple implicite a le publicmot - clé ... sinon vous obtiendrez une erreur
jharr100
Le CLR de Jeffrey Richter via C # 4 ed ch 13 montre un exemple où le casting n'est pas nécessaire: structure interne SomeValueType: IComparable {private Int32 m_x; public SomeValueType (Int32 x) {m_x = x; } public Int32 CompareTo (SomeValueType other) {...);} Int32 IComparable.CompareTo (Object other) {return CompareTo ((SomeValueType) other); }} public static void Main () {SomeValueType v = new SomeValueType (0); Object o = new Object (); Int32 n = v.CompareTo (v); // Pas de boxe n = v.CompareTo (o); // erreur de compilation}
Andy Dent
1
Aujourd'hui, j'ai rencontré une situation rare qui exigeait l'utilisation d'une interface explicite: une classe avec un champ généré par un constructeur d'interface qui crée le champ comme privé (Xamarin ciblant iOS, utilisant le storyboard iOS). Et une interface où il était logique d'exposer ce champ (public en lecture seule). J'aurais pu changer le nom du getter dans l'interface, mais le nom existant était le nom le plus logique pour l'objet. Ainsi , au lieu que je l'ai fait une mise en œuvre explicite faisant référence au domaine privé: UISwitch IScoreRegPlayerViewCell.markerSwitch { get { return markerSwitch; } }.
ToolmakerSteve
1
Les horreurs de la programmation. Eh bien, joliment démystifié!
Liquid Core
1
@ToolmakerSteve une autre situation (plutôt plus courante) qui nécessite une implémentation explicite d'au moins un membre d'interface implémente plusieurs interfaces qui ont des membres avec la même signature mais des types de retour différents. Cela peut se produire à cause de l' héritage d'interface, comme il le fait avec IEnumerator<T>.Current, IEnumerable<T>.GetEnumerator()et ISet<T>.Add(T). Ceci est mentionné dans une autre réponse .
phoog
201

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.

  1. En travaillant directement avec l'interface, vous ne reconnaissez pas et ne couplez pas votre code à l'implémentation sous-jacente.
  2. Dans le cas où vous avez déjà, disons, un nom de propriété publique dans votre code et que vous souhaitez implémenter une interface qui a également une propriété Name, le faire explicitement gardera les deux séparés. Même s'ils faisaient la même chose, je déléguerais toujours l'appel explicite à la propriété Name. Vous ne savez jamais, vous souhaiterez peut-être modifier le fonctionnement de Name pour la classe normale et le fonctionnement de Name, la propriété d'interface ultérieurement.
  3. Si vous implémentez une interface implicitement, votre classe expose maintenant de nouveaux comportements qui pourraient ne concerner qu'un client de l'interface et cela signifie que vous ne gardez pas vos classes suffisamment succinctes (à mon avis).
Phil Bennett
la source
2
Vous faites de bons points ici. surtout A. je passe généralement mes cours comme interface de toute façon, mais je n'y ai jamais vraiment pensé dans cette perspective.
mattlant
5
Je ne suis pas sûr d'être d'accord avec le point C. Un objet Cat pourrait implémenter IEatable mais Eat () est une partie de base. Il y aurait des cas où vous voudriez simplement appeler Eat () sur un chat lorsque vous utilisez l'objet «brut» plutôt que via l'interface IEatable, non?
LegendLength
59
Je connais des endroits où Catest en effet IEatablesans objections.
Humberto
26
Je suis complètement en désaccord avec tout ce qui précède, et je dirais que l'utilisation d'interfaces explicites est la recette du désastre et non de la POO ou de la DOO par leur définition (voir ma réponse sur le polymorphisme)
Valentin Kuzub
2
Voir aussi: stackoverflow.com/questions/4103300/…
Zack Jannsen
68

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:

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>implémente IEnumerable, 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.

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'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.

Matthew Scharley
la source
5
@Tassadaque: Après 2 ans, tout ce que je peux dire c'est "Bonne question". Je n'ai aucune idée, sauf peut-être que j'ai copié ce code à partir de quelque chose sur lequel je travaillais abstract.
Matthew Scharley
4
@Tassadaque, vous avez raison, mais je pense que le point du message de Matthew Scharley ci-dessus n'est pas perdu.
funkymushroom
35

Pour citer Jeffrey Richter de CLR via C #
( EIMI signifie E xplicit I nterface M ethod I mplementation )

Il est extrêmement important pour vous de comprendre certaines ramifications qui existent lors de l'utilisation d'EIMI. Et à cause de ces ramifications, vous devriez essayer d'éviter autant que possible les EIMI. Heureusement, les interfaces génériques vous aident à éviter les EIMI un peu. Mais il peut toujours y avoir des moments où vous devrez les utiliser (comme l'implémentation de deux méthodes d'interface avec le même nom et la même signature). Voici les gros problèmes avec les EIMI:

  • Il n'y a aucune documentation expliquant comment un type implémente spécifiquement une méthode EIMI, et il n'y a pas de prise en charge de Microsoft Visual Studio IntelliSense .
  • Les instances de type valeur sont encadrées lorsqu'elles sont converties en 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 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.

Valentin Kuzub
la source
2
La réimplémentation des interfaces, bien que légale, est généralement douteuse au mieux. Les implémentations explicites devraient généralement se chaîner à une méthode virtuelle soit directement, soit par le biais d'une logique d'encapsulation qui devrait être liée aux classes dérivées. Bien qu'il soit certainement possible d'utiliser des interfaces de manière hostile aux conventions de POO appropriées, cela ne signifie pas qu'elles ne peuvent pas être mieux utilisées.
supercat
4
@Valentin Que représentent EIMI et IEMI?
Dzienny
Implémentation de la méthode d'interface explicite
scobi
5
-1 pour "Généralement, je vois les interfaces comme une fonction POO semi (au mieux), elle fournit l'héritage, mais elle ne fournit pas de véritable polymorphisme." Je suis fortement en désaccord. Bien au contraire, les interfaces sont toutes sur le polymorphisme et ne sont pas principalement sur l'héritage. Ils attribuent plusieurs classifications à un type. Évitez IEMI, quand vous le pouvez, et déléguez, comme @supercat l'a suggéré, quand vous ne pouvez pas. N'évitez pas les interfaces.
Aluan Haddad
1
"Un EIMI ne peut pas être appelé par un type dérivé." << QUOI? Ce n'est pas vrai. Si j'implémente explicitement une interface sur un type, alors je dérive de ce type, je peux toujours le caster sur l'interface pour appeler la méthode, exactement comme je le devrais pour le type sur lequel il est implémenté. Vous ne savez donc pas de quoi vous parlez. Même dans le type dérivé, je peux simplement lancer "this" sur l'interface en question pour atteindre la méthode explicitement implémentée.
Triynko
34

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 :

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

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

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:

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

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.

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 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.

Jon Nadal
la source
19

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 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.

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 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é.

Lee Oades
la source
16

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:

  • Types de valeur, car cela nécessitera de la boxe et des performances inférieures. Ce n'est pas une règle stricte et dépend de l'interface et de la façon dont elle est destinée à être utilisée. IComparable? Implicite. IFormable? Probablement explicite.
  • Interfaces système triviales qui ont des méthodes qui sont souvent appelées directement (comme IDisposable.Dispose).

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:

  1. Ajoutez des publics et faites-leur parvenir les méthodes d'interface pour la mise en œuvre. Cela se produit généralement avec des interfaces plus simples lorsque vous travaillez en interne.
  2. (Ma méthode préférée) Ajoutez un public IMyInterface I { get { return this; } }(qui devrait être aligné) et appelez foo.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).
scobi
la source
8

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é.

Facture
la source
1
Belle réponse, Bill. Les autres réponses étaient excellentes, mais vous avez fourni des points de vue objectifs supplémentaires (en plus de vos opinions) qui m'ont facilité la compréhension. Comme la plupart des choses, il existe des avantages et des inconvénients avec les implémentations implicites ou explicites, il vous suffit donc d'utiliser la meilleure pour votre scénario ou votre cas d'utilisation. Je dirais que ceux qui essaient de mieux comprendre cela bénéficieront de la lecture de votre réponse.
Daniel Eagle
6

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.

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: implémentations d'interfaces implicites et explicites

Yochai Timmer
la source
6

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

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

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:

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

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 transtypage 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 un comportement utile sur certaines seulement [par exemple, le comportement spécifié pour la IList<T>.Addméthode d'une collection en lecture seule est de jeter NotSupportedException]. Malheureusement, la seule façon appropriée d'implémenter l'interface en C # est:

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

Pas aussi bien.

supercat
la source
2

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.

nrodic
la source
2

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'est public.

// Given:
internal interface I { void M(); }

// Then explicit implementation correctly observes encapsulation of I:
// Both ((I)CExplicit).M and CExplicit.M are accessible only internally.
public class CExplicit: I { void I.M() { } }

// However, implicit implementation breaks encapsulation of I, because
// ((I)CImplicit).M is only accessible internally, while CImplicit.M is accessible publicly.
public class CImplicit: I { public void M() { } }

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 publicaccès, même si l'interface elle-même est par exemple internal.

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 .

Marc Sigrist
la source