Was sind gute Gründe, die explizite Schnittstellenimplementierung ausschließlich zum Ausblenden von Mitgliedern zu verwenden?

11

Während einer meiner Studien zu den Feinheiten von C # stieß ich auf eine interessante Passage zur expliziten Implementierung von Schnittstellen.

While this syntax is quite helpful when you need to resolve name clashes, you can use explicit interface implementation simply to hide more "advanced" members from the object level.

Der Unterschied zwischen dem Erlauben object.method()oder dem Erfordernis des Gießens ((Interface)object).method()scheint für meine unerfahrenen Augen eine böse Verschleierung zu sein. Der Text darauf hingewiesen , dass dies die Methode von Intellisense auf Objektebene verbergen, aber warum sollen Sie wollen , das zu tun , wenn es nicht notwendig , Namenskonflikte zu vermeiden?

Nathanus
quelle

Antworten:

12

Stellen Sie sich eine Situation vor, in der eine Schnittstelle eine Klasse zwingt, Methoden zu implementieren, die als Teil der Klasse nicht sinnvoll sind. Dies kann häufig vorkommen, wenn die Schnittstellen besonders groß sind und gegen das Prinzip der Schnittstellentrennung verstoßen.

Diese Klasse muss die Schnittstelle implementieren, aber anstatt ihre gut sichtbare öffentliche Schnittstelle mit Methoden zu verschmutzen, die keinen Sinn ergeben, können Sie explizit die Methoden implementieren, die offensichtlich nicht verfügbar sein sollen. Die gut sichtbare öffentliche Schnittstelle der Klasse gibt an, wie die Klasse verwendet werden soll, und die explizite Implementierung ist vorhanden, wenn auf die Klasse über die Schnittstelle zugegriffen wird.

Chris Pitman
quelle
6
Bin ich es nur, oder klingt das nach einer schrecklichen Idee?
Kevin Peno
5
@ Kevin, wie so? Manchmal liegt die Schnittstelle außerhalb Ihrer Kontrolle und Sie müssen sie verwenden. Es scheint vernünftig, den Schaden einer überheblichen Schnittstelle zu mindern.
Chris Pitman
1
+1 für den Hinweis, dass die reale Welt oft der richtigen Vorgehensweise im Wege steht. @ Kevin ja, es ist eine schreckliche Idee, aber manchmal hat man keine Wahl und muss mit dem Slop arbeiten - besser, man versteckt ihn und macht eine Fassade, um Dinge richtig zu machen, wenn man es nicht wirklich richtig machen kann.
Wayne Molina
@ Wayne, ich verstehe Ihre Argumentation für den Wunsch / die Verwendung eines solchen Systems. Zur Hölle, ich würde es wahrscheinlich genau in dem Fall verwenden, den Sie angeben. Ich denke, mein kurzer Kommentar hat wirklich nur darauf hingewiesen, dass dies unangemessen ist. Warum also in der Sprache zulassen?
Kevin Peno
1
Eine übermäßige Trennung der Grenzflächen kann ein großes Hindernis für die Zusammensetzung und Aggregation sein. Stellen Sie sich zum Beispiel vor, dass eine Implementierung IEnumerable<T>gewünscht wird, die sich wie die Verkettung von zwei anderen verhält. Wenn IEnumerable<T>eine Eigenschaft enthalten ist, die angibt, ob ihre Anzahl bekannt ist, möglicherweise unendlich ist oder keine, und eine Methode, um zu fragen, wie hoch die Anzahl ist, kann eine Klasse, die mehrere aggregiert IEnumerable<T>und sich wie eine Verkettung verhält, ihre Anzahl effizient melden, falls einige von Die gekapselten Sammlungen kannten ihre Anzahl.
Supercat
4

Die nützlichste Anwendung, die ich gefunden habe, ist die Implementierung von Fabriken. In vielen Fällen ist es nützlich, Klassen zu erstellen, die innerhalb der Factory veränderbar, aber für externe Klassen unveränderlich sind. Dies kann einfach in Java mithilfe innerer Klassen implementiert werden, indem die Felder und ihre Setter privat gemacht werden und die Getter nur als öffentliche Mitglieder verfügbar gemacht werden. In C # musste ich jedoch explizite Schnittstellen verwenden, um dasselbe zu erreichen. Ich werde weiter erklären:

In Java können die innere Klasse UND die äußere Klasse auf private Mitglieder zugreifen, was absolut sinnvoll ist, da die Klassen sehr eng miteinander verbunden sind. Sie befinden sich in derselben Codedatei und werden wahrscheinlich von demselben Entwickler entwickelt. Dies bedeutet, dass die Fabrik weiterhin auf private Felder und Methoden in der inneren Klasse zugreifen kann, um deren Werte zu ändern. Externe Klassen können jedoch nur über ihre öffentlichen Getter auf diese Felder zugreifen.

In C # können äußere Klassen jedoch nicht auf private Mitglieder innerer Klassen zugreifen, sodass das Konzept nicht direkt anwendbar ist. Ich habe eine explizite Schnittstelle als Problemumgehung verwendet, indem ich eine private Schnittstelle in der äußeren Klasse definiert und explizit in der inneren Klasse implementiert habe. Auf diese Weise kann nur die äußere Klasse auf die Methoden in dieser Schnittstelle zugreifen, wie dies in Java der Fall ist (es müssen jedoch Methoden sein, keine Felder).

Beispiel:

public class Factory
{
    // factory method to create a hard-coded Mazda Tribute car.
    public static Car CreateCar()
    {
        Car car = new Car();

        // the Factory class can modify the model because it has access to
        // the private ICarSetters interface
        ((ICarSetters)car).model = "Mazda Tribute";

        return car;
    }

    // define a private interface containing the setters.
    private interface ICarSetters
    {
        // define the setter in the private interface
        string model { set; }
    }

    // This is the inner class. It has a member "model" that should not be modified
    // but clients, but should be modified by the factory.
    public class Car: ICarSetters
    {
        // explicitly implement the setter
        string ICarSetters.model { set; }

        // create a public getter
        public string model { get; }
    }
}

class Client
{
    public Client()
    {
        Factory.Car car = Factory.CreateCar();

        // can only read model because only the getter is public
        // and ICarSetters is private to Factory
        string model = car.model;
    }
}

Dafür würde ich explizite Schnittstellen verwenden.

Elias
quelle
3

Wenn eine Klasse zwei Schnittstellen implementiert, die ein Mitglied mit derselben Signatur enthalten, führt die Implementierung dieses Mitglieds in der Klasse dazu, dass beide Schnittstellen dieses Mitglied als Implementierung verwenden. Im folgenden Beispiel Paintrufen alle Aufrufe dieselbe Methode auf. C #

class Test 
{
    static void Main()

    {
        SampleClass sc = new SampleClass();
        IControl ctrl = (IControl)sc;
        ISurface srfc = (ISurface)sc;

        // The following lines all call the same method.
        sc.Paint();
        ctrl.Paint();
        srfc.Paint();
    }
}


interface IControl
{
    void Paint();
}
interface ISurface
{
    void Paint();
}
class SampleClass : IControl, ISurface
{
    // Both ISurface.Paint and IControl.Paint call this method. 
    public void Paint()
    {
        Console.WriteLine("Paint method in SampleClass");
    }
}

Im Gegensatz dazu können Sie durch die explizite Implementierung eines (oder beider) von ihnen für jedes ein anderes Verhalten angeben.

Muhammad Tayyeb
quelle
1

Explizite Schnittstellen sind praktisch, wenn ein Objekt zwei oder mehr sehr unterschiedliche Kommunikationsformen aufweist.

Beispiel: Ein Objekt, das eine Sammlung von untergeordneten Objekten verwaltet, die mit dem übergeordneten Objekt kommunizieren müssen.

Es gibt einige Aktionen, auf die nur externe Anrufer zugreifen sollen, und einige Aktionen, auf die nur untergeordnete Objekte zugreifen sollen.

Explizite Schnittstellen tragen dazu bei, diese Kluft zu gewährleisten.

    static void Main(string[] args)
    {
        var parent = new Parent();
        parent.DoExternalStuff();
    }

    interface IExternal {
        void DoExternalStuff();
    }

    interface IInternal {
        void DoInternalStuff();
    }

    class Parent : IExternal, IInternal
    {
        public Parent()
        {
            var child = new Child(this);
        }

        public void DoExternalStuff()
        {
           // do something exciting here for external callers
        }

        void IInternal.DoInternalStuff()
        {
            // do something exciting here for internal callers
        }
    }

    class Child
    {
        public Child(IInternal parent)
        {
            parent.DoInternalStuff();
        }
    }
lzcd
quelle
0

Vielleicht, wenn Sie Funktionen hatten, die für die Benutzer nützlich waren, aber nur, wenn Sie wirklich wissen, was Sie tun (genug, um die Dokumentation gelesen zu haben, um mehr über die Funktion zu erfahren), aber das kann wahrscheinlich zu Problemen führen, wenn Sie dies nicht tun.

Warum Sie das tun würden, anstatt eine zweite "Advanced Features Interface" bereitzustellen, weiß ich nicht.

Malcolm
quelle
0

Code wird im Allgemeinen mehr gelesen als geschrieben und ich verwende Intellisense nur, um Code schnell zu schreiben. Ich würde keinen bestimmten Code schreiben, um Intellisense schöner zu machen.

Ich sehe nicht besonders wie

((Schnittstellen-) Objekt) .method () ist nicht lesbarer als object.method ().

Wenn ich viele Methoden für ein bestimmtes Objekt hätte, könnte ich mich fragen, ob es ein Gottobjekt ist oder nicht und sollte tatsächlich in mehrere einfachere Objekte aufgeteilt werden.

Peter Smith
quelle