684 Stimmen

C#-Schnittstellen. Implizite Implementierung versus explizite Implementierung

Was sind die Unterschiede bei der Implementierung von Schnittstellen? implizit y ausdrücklich in C#?

Wann sollten Sie implizit und wann explizit verwenden?

Gibt es Vor- und/oder Nachteile für das eine oder das andere?


Die offiziellen Richtlinien von Microsoft (aus der ersten Ausgabe Rahmengestaltungsrichtlinien ) besagt, dass die Verwendung expliziter Implementierungen wird nicht empfohlen , da der Code dadurch ein unerwartetes Verhalten zeigt.

Ich denke, diese Leitlinie ist sehr gültig in einer Vor-IoC-Zeit wenn Sie die Dinge nicht als Schnittstellen weitergeben.

Könnte jemand auch auf diesen Aspekt eingehen?

0 Stimmen

Vollständigen Artikel über C#-Schnittstellen lesen : planetofcoders.com/c-schnittstellen

1 Stimmen

Ja, explizite Schnittstellen sollten vermieden werden, und ein professionellerer Ansatz wäre die Implementierung von ISP (Schnittstellentrennungsprinzip) - hier ein ausführlicher Artikel dazu codeproject.com/Artikel/1000374/

0 Stimmen

In den meisten Fällen werden Schnittstellen nicht benötigt, denken Sie darüber nach, wer Ihren Code verwendet, niemand, also verschwenden Sie keine Zeit mit dem Versuch, die besten Schnittstellen zu implementieren, Schnittstellen erhöhen nur die Komplexität des Projekts und führen zu mehr Funktionsfehlern, als es sein sollte

526voto

mattlant Punkte 15146

Implizit ist, wenn Sie Ihre Schnittstelle über ein Mitglied Ihrer Klasse definieren. Ausdrücklich ist, wenn Sie Methoden innerhalb Ihrer Klasse auf der Schnittstelle definieren. Ich weiß, das hört sich verwirrend an, aber ich sage Ihnen, was ich meine: IList.CopyTo würde implizit als implementiert werden:

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

und ausdrücklich als:

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

Der Unterschied besteht darin, dass die implizite Implementierung den Zugriff auf die Schnittstelle über die von Ihnen erstellte Klasse ermöglicht, indem Sie die Schnittstelle als diese Klasse und als die Schnittstelle selbst casten. Bei der expliziten Implementierung können Sie nur auf die Schnittstelle zugreifen, indem Sie sie als Schnittstelle selbst casten.

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

Ich verwende explicit hauptsächlich, um die Implementierung sauber zu halten, oder wenn ich zwei Implementierungen benötige. Unabhängig davon verwende ich es selten.

Ich bin mir sicher, dass es noch weitere Gründe gibt, die für oder gegen eine explizite Verwendung sprechen, die von anderen genannt werden.

Siehe die nächster Beitrag in diesem Thread für eine ausgezeichnete Argumentation hinter jedem.

212voto

Phil Bennett Punkte 4733

Die implizite Definition würde darin bestehen, die von der Schnittstelle geforderten Methoden/Eigenschaften usw. direkt als öffentliche Methoden in die Klasse aufzunehmen.

Die explizite Definition erzwingt, dass die Mitglieder nur dann offengelegt werden, wenn Sie direkt mit der Schnittstelle arbeiten, und nicht mit der zugrunde liegenden Implementierung. Dies ist in den meisten Fällen vorzuziehen.

  1. Wenn Sie direkt mit der Schnittstelle arbeiten, erkennen Sie dies nicht an, und koppeln Ihren Code nicht an die zugrunde liegende Implementierung.
  2. Für den Fall, dass Sie bereits, sagen wir, eine öffentliche Eigenschaft Name in Code haben und eine Schnittstelle implementieren wollen, die ebenfalls eine Name-Eigenschaft hat, können Sie diese beiden Eigenschaften explizit voneinander trennen. Auch wenn sie dasselbe tun, würde ich den expliziten Aufruf der Aufruf an die Name-Eigenschaft delegieren. Man kann nie wissen, ob man nicht vielleicht ändern möchte wie Name für die normale Klasse funktioniert und wie Name, die Interface Eigenschaft später funktioniert.
  3. Wenn Sie eine Schnittstelle implizit implementieren, stellt Ihre Klasse nun neue Verhaltensweisen, die möglicherweise nur für einen Client der Schnittstelle relevant sind, und das bedeutet, dass Sie Ihre Klassen nicht prägnant genug (meine Meinung).

74voto

Matthew Scharley Punkte 121038

Zusätzlich zu den hervorragenden Antworten, die bereits gegeben wurden, gibt es einige Fälle, in denen eine explizite Implementierung ERFORDERLICH ist, damit der Compiler in der Lage ist, herauszufinden, was erforderlich ist. Werfen Sie einen Blick auf IEnumerable<T> als Paradebeispiel, das wahrscheinlich ziemlich oft vorkommen wird.

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 und deshalb müssen wir das auch tun. Aber Moment mal, sowohl die generische als auch die normale Version beide implementieren Funktionen mit der gleichen Methodensignatur (C# ignoriert hierfür den Rückgabetyp). Dies ist völlig legal und in Ordnung. Wie entscheidet der Compiler, welcher Typ zu verwenden ist? Er zwingt Sie, höchstens eine implizite Definition zu haben, dann kann er auflösen, was immer er braucht.

d. h.

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: Die kleine Umleitung in der expliziten Definition für IEnumerable funktioniert, weil der Compiler innerhalb der Funktion weiß, dass der tatsächliche Typ der Variablen eine StringList ist, und so löst er den Funktionsaufruf auf. Eine nette kleine Tatsache für die Implementierung einiger der Abstraktionsebenen, die einige der .NET-Kernschnittstellen angesammelt zu haben scheinen.

35voto

Valentin Kuzub Punkte 10967

Um Jeffrey Richter von CLR via C# zu zitieren
( EIMI bedeutet E xplicit I nterface M ethode I mUmsetzung)

Es ist von entscheidender Bedeutung, dass Sie einige Verzweigungen zu verstehen, die die bei der Verwendung von EIMI bestehen. Und wegen dieser dieser Verzweigungen EIMIs so weit wie möglich zu vermeiden. Glücklicherweise helfen generische Schnittstellen Sie EIMIs weitgehend vermeiden. Aber es kann es trotzdem vorkommen, dass Sie zu verwenden (z. B. bei der Implementierung von zwei Schnittstellenmethoden mit demselben Namen und Signatur). Hier sind die großen Probleme mit EIMIs:

  • Es gibt keine Dokumentation, die erklärt, wie ein Typ speziell eine EIMI-Methode implementiert, und es gibt [ ] IntelliSense Unterstützung.
  • Wertetyp-Instanzen werden beim Casting auf eine Schnittstelle mit einem Rahmen versehen.
  • Ein EIMI kann nicht von einem abgeleiteten Typ aufgerufen werden.

Wenn Sie eine Schnittstellenreferenz verwenden, kann JEDE virtuelle Kette in jeder abgeleiteten Klasse explizit durch EIMI ersetzt werden, und wenn ein Objekt eines solchen Typs auf die Schnittstelle gecastet wird, wird Ihre virtuelle Kette ignoriert und die explizite Implementierung aufgerufen. Das ist alles andere als Polymorphismus.

EIMIs können auch verwendet werden, um nicht stark typisierte Schnittstellenmitglieder vor den Implementierungen von Basis-Framework-Schnittstellen wie IEnumerable<T> zu verstecken, so dass Ihre Klasse nicht direkt eine nicht stark typisierte Methode offenlegt, aber syntaktisch korrekt ist.

35voto

Jon Nadal Punkte 711

Grund Nr. 1

Ich neige dazu, explizite Schnittstellenimplementierungen zu verwenden, wenn ich vom "Programmieren auf eine Implementierung" abraten möchte ( Entwurfsprinzipien von Entwurfsmustern ).

Zum Beispiel, in einem MVP -basierte Webanwendung:

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

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

Nun kann eine andere Klasse (z. B. eine Präsentator ) ist weniger von der StandardNavigator-Implementierung als vielmehr von der INavigator-Schnittstelle abhängig (da die Implementierung in eine Schnittstelle umgewandelt werden müsste, um die Redirect-Methode nutzen zu können).

Grund #2

Ein weiterer Grund für die Verwendung einer expliziten Schnittstellenimplementierung wäre, die "Standard"-Schnittstelle einer Klasse sauberer zu halten. Wenn ich zum Beispiel eine Klasse entwickeln würde ASP.NET Server-Kontrolle, möchte ich vielleicht zwei Schnittstellen:

  1. die primäre Schnittstelle der Klasse, die von Webseitenentwicklern verwendet wird; und
  2. Eine "verborgene" Schnittstelle, die vom Moderator verwendet wird und von mir entwickelt wurde, um die Logik des Steuerelements zu handhaben

Es folgt ein einfaches Beispiel. Es handelt sich um ein Kombinationsfeld-Steuerelement, das Kunden auflistet. In diesem Beispiel ist der Webseitenentwickler nicht daran interessiert, die Liste aufzufüllen; stattdessen möchte er nur in der Lage sein, einen Kunden nach GUID auszuwählen oder die GUID des ausgewählten Kunden zu erhalten. Ein Presenter würde das Feld beim ersten Laden der Seite auffüllen, und dieser Presenter ist durch das Steuerelement 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 auf, und der Webseitenentwickler braucht von ihrer Existenz nichts zu wissen.

Aber es ist keine silberne Kanonenkugel

Ich würde nicht empfehlen, immer explizite Schnittstellenimplementierungen zu verwenden. Dies sind nur zwei Beispiele, bei denen sie hilfreich sein könnten.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X