C # Schnittstellen. Implizite Implementierung im Vergleich zu Explizite Implementierung

StackOverflow https://stackoverflow.com/questions/143405

  •  02-07-2019
  •  | 
  •  

Frage

Was sind die Unterschiede bei der Umsetzung der Schnittstellen implizit und explizit in C #?

Wenn Sie verwenden sollten implizite und wann sollten Sie explizit verwenden?

Gibt es irgendwelche Vor- und / oder Nachteile der einen oder anderen?


Microsofts offiziellen Richtlinien (von der ersten Ausgabe Framework Design Guidelines ) besagt, dass < strong> sind explizite Implementierungen mit nicht empfohlen , da sie den Code ein unerwartetes Verhalten gibt.

Ich denke, diese Richtlinie ist sehr gültig in einer vorge IoC-Zeit , wenn Sie die Dinge nicht als Schnittstellen laufen um.

Könnte jemand auf diesen Aspekt berühren auch?

War es hilfreich?

Lösung

Implizite , wenn Sie Ihre Schnittstelle über ein Element auf Ihrer Klasse definieren. Explicit ist, wenn Sie Methoden in Ihrer Klasse auf der Schnittstelle zu definieren. Ich weiß, das klingt verwirrend, aber hier ist das, was ich meine: IList.CopyTo würde implizit umgesetzt werden:

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

und explizit als:

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

Der Unterschied, dass implizit zu sein ist über Ihre Klasse, die Sie erstellt, wenn es als diese Klasse gegossen sowie bei seiner Besetzung als Schnittstelle. Explizite Implementierung erlaubt es nur zugänglich, wenn die Schnittstelle selbst gegossen.

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

Ich verwende ausdrücklich in erster Linie die Umsetzung sauber zu halten, oder wenn ich brauche zwei Implementierungen. Aber unabhängig selten ich es verwenden.

Ich bin sicher, dass es mehr Gründe, es zu benutzen / verwenden Sie es nicht, dass andere veröffentlichen.

Sehen Sie die Der nächste Eintrag in diesem Thread für hervorragende Argumentation hinter jedem.

Andere Tipps

Implizite Definition wäre, nur die Methoden / Eigenschaften hinzufügen usw. durch die Schnittstelle gefordert direkt auf die Klasse als öffentliche Methoden.

Explizite Definition zwingt die Mitglieder nur ausgesetzt werden, wenn Sie mit der Schnittstelle direkt arbeiten, und nicht die zugrunde liegende Implementierung. Dies ist in den meisten Fällen bevorzugt.

  1. Durch die direkt mit der Schnittstelle arbeiten, bestätigen Sie nicht, und Koppeln Sie den Code an die zugrunde liegende Implementierung.
  2. Für den Fall, dass Sie bereits haben, sagen wir, eine öffentliche Eigenschaft Name in Ihr Code und Sie möchten eine Schnittstelle implementieren, die auch eine hat Name-Eigenschaft, tut es wird explizit die zwei separate halten. Sogar wenn sie die gleiche Sache tun würde ich delegieren noch die explizite rufen Sie an die Eigenschaft Name. Man kann nie wissen, können Sie sich ändern wie Name des für die normale Klasse funktioniert und wie Namen der Schnittstelle Eigenschaft funktioniert später.
  3. Wenn Sie eine Schnittstelle implementieren implizit dann jetzt Ihre Klasse macht neue Verhaltensweisen, die an einen Kunden von der Bedeutung sein könnten nur Schnittstelle und es bedeutet, dass Sie nicht halten Sie Ihre Klassen prägnant sind genug (meiner Meinung nach).

Neben hervorragenden Antworten bereits vorgesehen ist, gibt es einige Fälle, in denen explizite Implementierung für den Compiler ist ERFORDERLICH der Lage sein, um herauszufinden, was erforderlich ist. Werfen Sie einen Blick auf IEnumerable<T> als Paradebeispiel, die wahrscheinlich kommen wird ziemlich oft.

Hier ist ein Beispiel:

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
}

Hier IEnumerable<string> implementiert IEnumerable, daher müssen wir auch an. Aber Moment mal, sowohl die allgemeinen und die normale Version beide implementieren Funktionen mit der gleichen Methode Signatur (C # ignoriert für diese Rückgabetyp). Dies ist völlig legal und in Ordnung. Wie löst der Compiler, der zu benutzen? Es zwingt Sie nur haben, höchstens eine implizite Definition, dann kann es beheben, was auch immer es muss.

dh.

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: Das kleine Stück Indirektionsebene in der expliziten Definition für IEnumerable arbeitet in der Funktion, da der Compiler weiß, dass der tatsächliche Typ der Variablen ein String ist, und das ist, wie es den Funktionsaufruf löst. Nifty wenig Tatsache für einige der Abstraktionsschichten einen Teil der .NET-Kernschnittstellen scheinen akkumulierte Implementierung zu haben.

Grund # 1

Ich neige dazu, explizite Schnittstellenimplementierung zu verwenden, wenn ich „Programmierung auf eine Implementierung“ ( Design-Prinzipien von Design Patterns ).

Zum Beispiel in einer MVP -basierte Web-Anwendung:

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

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

Nun eine andere Klasse (wie ein Moderator ) ist weniger wahrscheinlich, dass auf der StandardNavigator Implementierung abhängig und eher auf der INavigator Schnittstelle abhängen (da die Implementierung eine Schnittstelle umgewandelt werden müßte Verwendung der Redirect-Methode zu machen).

Grund # 2

Ein weiterer Grund, warum ich mit einer expliziten Interface-Implementierung gehen könnte, wäre eine „default“ Schnittstelle Reiniger Klasse zu halten. Zum Beispiel, wenn ich ein ASP.NET Server Kontrolle wurde entwickeln, könnte ich zwei Schnittstellen will :

  1. Die primäre Schnittstelle der Klasse, die von Web-Seite Entwickler verwendet wird; und
  2. Eine „versteckte“ Schnittstelle des Moderators verwendet, die ich entwickeln, um die Logik des Steuergriffes

Ein einfaches Beispiel folgt. Es ist ein Kombinationsfeld-Steuerelement, die Kunden auflistet. In diesem Beispiel ist die Web-Seite Entwickler in bevölkern die Liste nicht interessiert; Stattdessen wollen sie nur in der Lage sein, einen Kunden von GUID zu wählen oder die ausgewählten Kunden GUID zu erhalten. Ein Moderator würde die Box auf der ersten Seite zu laden bevölkert, und dieser Präsentator wird von der Steuerung gekapselt.

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; }
}

Der Präsentator füllt die Datenquelle und der Web-Seite Entwickler braucht, nie bewusst seine Existenz sein.

Aber ist es ist nicht ein Silber Hock

Ich würde nicht immer unter Verwendung von expliziten Schnittstellenimplementierungen empfehlen. Das sind nur zwei Beispiele, wo sie nützlich sein könnte.

Jeffrey Richter von CLR via C # zu zitieren
( EIMI Mittel E xplicit I nterface M ethode I UMSETZUNG)

  

Es ist von entscheidender Bedeutung für Sie   verstehen einige Konsequenzen, die   existieren, wenn EIMIs verwenden. Und wegen   diese Auswirkungen, sollten Sie versuchen,   vermeiden EIMIs so viel wie möglich.   Glücklicherweise generische Schnittstellen helfen   Sie vermeiden EIMIs ziemlich viel. Aber dort   kann noch mal sein, wenn Sie brauchen   zu verwenden, um sie (wie die Umsetzung zwei   Interface-Methoden mit dem gleichen Namen   und Unterschrift). Hier sind die großen   Probleme mit EIMIs:

     
      
  • Es gibt keine Dokumentation zu erklären, wie eine Art speziell   implementiert ein EIMI Verfahren, und es   ist kein Microsoft Visual Studio    IntelliSense Unterstützung.
  •   
  • Werttyp-Instanzen sind eingerahmt, wenn sie eine Schnittstelle umgewandelt.
  •   
  • Ein EIMI kann nicht von einem abgeleiteten Typ genannt werden.
  •   

Wenn Sie eine Interface-Referenz ANY virtuelle Kette kann explizit mit EIMI auf jeder abgeleiteten Klasse ersetzt werden und wenn ein Objekt dieser Art in die Schnittstelle umgewandelt wird, wird die virtuelle Kette ignoriert und die explizite Implementierung aufgerufen. Das ist alles andere als Polymorphismus.

EIMIs können auch verstecken nicht stark typisierte Schnittstelle Mitglieder aus Grundrahmen Interfaces' Implementierungen wie IEnumerable verwendet werden, so dass Ihre Klasse eine nicht stark typisierte Methode nicht direkt ausgesetzt werden, ist aber syntaktisch korrekt ist.

Zusätzlich zu den anderen bereits genannten Gründen, ist dies die Situation, in der eine Klasse zwei verschiedene Schnittstellen implementiert, die eine Eigenschaft / Methode mit dem gleichen Namen und Unterschrift hat.

/// <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
}

Dieser Code kompiliert und läuft OK, aber die Eigenschaft Titel geteilt wird.

Natürlich würden wir den Wert des Titels wollen zurück auf abhängen, ob wir Class1 als ein Buch oder einer Person behandeln. Dies ist, wenn wir die explizite Schnittstelle verwenden können.

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

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

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

Beachten Sie, dass die expliziten Schnittstellendefinitionen abgeleitet werden öffentlich sein - und daher kann man sich nicht erklärt öffentlich zu sein (oder anders) explizit

.

Beachten Sie auch, dass Sie immer noch eine „shared“ Version haben kann (wie oben dargestellt), aber während dies möglich ist, die Existenz einer solchen Eigenschaft ist fraglich. Vielleicht ist es als Standardimplementierung des Titels verwendet werden könnten -. So, dass die bestehenden Code würde nicht geändert werden müssen Class1 zu IBook oder IPerson werfen

Wenn Sie definieren nicht die "shared" (impliziten) Titel, die Verbraucher von Class1 muss Sie explizit Instanzen von Class1 zu IBook oder IPerson ersten -. Andernfalls wird der Code nicht kompilieren

Ich verwende explizite Schnittstellenimplementierung die meiste Zeit. Hier sind die wichtigsten Gründe.

Refactoring ist sicherer

Wenn Sie eine Schnittstelle zu ändern, ist es besser, wenn die Compiler es überprüfen können. Das ist schwieriger, mit impliziten Implementierungen.

Zwei gemeinsame Fälle in den Sinn kommen:

  • eine Funktion Hinzufügen eine Schnittstelle, wo eine bestehende Klasse, die diese Schnittstelle bereits geschieht implementiert ein Verfahren mit der gleichen Signatur wie die neuen haben . Dies kann zu unerwartetem Verhalten führen, und hat mich hart mehrmals gebissen. Es ist schwierig, zu „sehen“ beim Debuggen, da diese Funktion wahrscheinlich nicht mit den anderen Interface-Methoden in der Datei (die selbstdokumentiere Ausgabe erwähnt unten).

  • befand
  • eine Funktion aus einer Schnittstelle entfernen . Implizit implementierten Methoden werden plötzlich tot Code, sondern explizit implementierten Methoden werden von Compiler-Fehler verfangen. Auch wenn der tote Code gut ist, um zu halten, möchte ich es zu überprüfen gezwungen werden und es zu fördern.

Es ist bedauerlich, dass C # nicht ein Schlüsselwort hat, das uns zwingt, eine Methode als eine implizite Umsetzung zu markieren, so dass der Compiler die zusätzliche Kontrollen tun könnte. Virtuelle Methoden nicht eine der beiden oben genannten Probleme aufgrund der erforderlichen Verwendung von ‚Überschreibung‘ haben und ‚neu‘.

Hinweis: für feste oder selten wechselnde Schnittstellen (in der Regel von Anbietern APIs), ist dies kein Problem. Für meine eigene Schnittstellen, obwohl, ich kann nicht vorhersagen, wann / wie sie sich verändern wird.

Es ist selbsterklärend

Wenn ich sehe, ‚public bool Execute ()‘ in einer Klasse, es zusätzliche Arbeit dauern wird, um herauszufinden, dass sie Teil einer Schnittstelle ist. Jemand wird es wahrscheinlich zu äußern sagt so, oder es ausdrückte in einer Gruppe anderer Interface-Implementierungen, die alle unter einer Region oder Gruppierung Kommentar sagen: „Implementierung von ITask“. Natürlich, das funktioniert nur, wenn der Gruppenkopf ist nicht außerhalb des Bildschirms ..

Während:. Boolsche ITask.Execute () 'ist klar und eindeutig

Klare Trennung von Interface-Implementierung

Ich denke an Schnittstellen mehr sein ‚Öffentlichkeit‘ als öffentliche Methoden, weil sie gefertigt sind nur ein wenig von der Oberfläche der Beton Art zu belichten. Sie reduzieren die Art zu einer Fähigkeit, ein Verhalten, eine Reihe von Merkmalen, etc. Und in der Umsetzung, ich denke, dass es nützlich ist, um diese Trennung zu halten.

Als ich durch eine Klasse Code suchen, wenn ich über explizite Interface-Implementierungen kommen, mein Gehirn verschiebt in „Code Vertrag“ -Modus. Oft werden diese Implementierungen einfach vorwärts zu anderen Methoden, aber manchmal werden sie zusätzliche Zustand / param Prüfung tun, die Umwandlung der eingehenden Parameter besser auf interne Anforderungen entsprechen, oder auch Übersetzung für Versionierung Zwecke (dh mehrere Generationen von Schnittstellen alle bis auf gemeinsamen Implementierungen Tochern).

(Ich weiß, dass Öffentlichkeiten sind auch Code Verträge, aber Schnittstellen sind viel stärker, vor allem in einer Interface-driven Code-Basis, wo die direkte Verwendung von Betontypen ist in der Regel ein Zeichen des nur die internen Code).

Siehe auch:. Grund 2 oben von Jon

Und so weiter

Plus die Vorteile bereits in anderen Antworten erwähnt hier:

Probleme

Es ist nicht alles Spaß und Glück. Es gibt einige Fälle, in denen ich mit implicits halten:

  • Werttypen, denn das wird Boxen und untere perf erfordern. Dies ist keine strenge Regel, und ist abhängig von der Schnittstelle und wie es verwendet werden soll. IComparable? Implizit. IFormattable? wahrscheinlich explicit.
  • Trivial-System-Schnittstellen, die Methoden, die häufig direkt aufgerufen werden (wie IDisposable.Dispose).

Auch kann es ein Schmerz, das Casting zu tun, wenn Sie in der Tat die konkrete Art und wollen eine explizite Schnittstelle Methode aufzurufen. Ich beschäftige mich mit diesem in einer von zwei Möglichkeiten:

  1. Fügen Sie Öffentlichkeiten und haben die Interface-Methoden nach vorn, um sie für die Umsetzung. Typischerweise mit einfachen Schnittstellen geschieht, wenn intern arbeitet.
  2. In
  3. (Meine bevorzugte Methode) eine public IMyInterface I { get { return this; } } (die bekommen inlined sollte) und rufen foo.I.InterfaceMethod(). Wenn mehrere Schnittstellen, die diese Fähigkeit benötigen, den Namen jenseits ich erweitern (in meiner Erfahrung selten es ist, dass ich dieses Bedürfnis haben).

Wenn Sie explizit implementieren, werden Sie nur in der Lage sein, die Schnittstelle Mitglieder über einen Verweis zu verweisen, das von der Art der Schnittstelle ist. Eine Referenz, die die Art der implementierenden Klasse ist, wird diese Schnittstelle Mitglieder nicht aus.

Wenn Sie Ihre Implementierungsklasse nicht öffentlich ist, mit Ausnahme der Methode verwendet, um die Klasse zu erstellen (die ein Werk sein könnten oder

Jeder Teilnehmer, die eine Schnittstelle implementiert exportiert eine Erklärung, die auf den Weg VB.NET Schnittstelle Erklärungen geschrieben semantisch ähnlich sind, z.

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

Auch wenn der Name des Klassenmitgliedes oft, dass das Schnittstellenelementes entspricht, und die Klasse Mitglied wird oft öffentlich, ist keines dieser Dinge erforderlich. Man kann auch erklären:

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

In diesem Fall wird die Klasse und ihre Derivate würden erlaubt einen Teilnehmer für den Zugriff auf den Namen IFoo_Foo verwenden, aber die Außenwelt würde nur in der Lage sein, dass bestimmtes Mitglied zugreifen, indem er auf IFoo Gießen. Ein solcher Ansatz ist oft gut in Fällen, in denen ein Schnittstellenverfahren haben wird angegeben Verhalten auf allen Implementierungen, aber nützlich Verhalten auf nur einige [z das angegebene Verhalten für eine Nur-Lese-IList<T>.Add Methode der Sammlung ist NotSupportedException] zu werfen. Leider ist der einzig richtige Weg, um die Schnittstelle in C # zu implementieren ist:

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

Nicht so schön.

Eine wichtige Verwendung von expliziter Schnittstellenimplementierung ist, wenn in der Notwendigkeit zu implementieren Schnittstellen mit gemischter Sichtbarkeit .

Das Problem und die Lösung sind in dem Artikel gut erklärt C # Interne Schnittstelle .

Wenn Sie beispielsweise das Austreten von Objekten zwischen Anwendungsschichten schützen möchten, ist diese Technik ermöglicht es Ihnen, unterschiedliche Sicht der Mitglieder an, die die Leckage führen könnte.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top