Sind Delegierte nicht nur Kurzschnittstellen?

Angenommen wir haben:

interface Foo 
{
 bool Func(int x);
}

class Bar: Foo
{
  bool Func(int x)
  {
   return (x>0);
  }  
}

class Baz: Foo
{
  bool Func(int x)
  {
   return (x<0);
  }  
}

Jetzt können wir Bar und Baz als Foos herumwerfen und ihre Func-Methoden aufrufen.

Die Delegierten vereinfachen dies ein wenig:

delegate bool Foo(int x);

bool Bar(int x)
{
 return (x<0);
}

bool Baz(int x)
{
 return (x>0);
}

Jetzt können wir Bar und Baz als Foo-Delegierte herumwerfen.

Was ist der wirkliche Vorteil der Delegierten, außer dass sie weniger Code bekommen?

0

10 Antworten

Nein, Delegierte sind für Methodenzeiger. Dann können Sie sicherstellen, dass die Signatur der mit dem Delegaten verknüpften Methode korrekt ist.

Außerdem müssen Sie die Struktur der Klasse nicht kennen. Auf diese Weise können Sie eine Methode verwenden, die Sie geschrieben haben, um sie an eine Methode in einer anderen Klasse zu übergeben, und die Funktionalität definieren, die Sie ausführen möchten.

Take a look at the List<> class with the Find method. Now you get to define what determines if something is a match or not, without requiring items contained in the class to implement IListFindable or something similar.

0
hinzugefügt

Ein Delegat ist ein typisierter Methodenzeiger. Dies gibt Ihnen mehr Flexibilität als Schnittstellen, da Sie Kovarianz und Kontravarianz nutzen können, und Sie können den Objektstatus ändern (Sie müssten den this-Zeiger mit Interface-basierten Funktoren übergeben).

Delegierte haben auch viele schöne syntaktische Zucker, mit denen Sie Dinge leicht kombinieren können.

0
hinzugefügt

In mindestens einem Vorschlag zum Hinzufügen von Closures (d.h. anonymen Delegaten) zu Java sind sie äquivalent zu Schnittstellen mit einer einzigen Mitgliedsmethode.

0
hinzugefügt

Man kann sich Delegaten als eine Schnittstelle für eine Methode vorstellen, die definiert, welche Argumente und welchen Rückgabetyp eine Methode haben muss, um zu dem Delegaten zu passen

0
hinzugefügt

Ein Delegat hat viele Gemeinsamkeiten mit einer Schnittstellenreferenz, die aus Sicht des Aufrufers eine einzige Methode hat.

Im ersten Beispiel sind Baz und Bar Klassen, die vererbt und instanziiert werden können. Im zweiten Beispiel sind Baz und Bar Methoden.

Sie können keine Schnittstellenreferenzen auf jede Klasse anwenden, die dem Schnittstellenvertrag entspricht. Die Klasse muss explizit deklarieren, dass sie die Schnittstelle unterstützt. Sie können eine Delegatenreferenz auf jede Methode anwenden, die der Signatur entspricht.

Sie können keine statischen Methoden in den Vertrag einer Schnittstelle aufnehmen. (Obwohl Sie statische Methoden mit Erweiterungsmethoden aktivieren können). Sie können auf statische Methoden mit einer Delegatenreferenz verweisen.

0
hinzugefügt

Sie können Delegaten als Parameter in Funktionen übergeben (Ok technisch Delegaten werden Objekte, wenn kompiliert, aber das ist nicht der Punkt hier). Sie könnten ein Objekt als Parameter übergeben (offensichtlich), aber dann binden Sie diesen Objekttyp an die Funktion als Parameter. Mit Delegaten können Sie jede Funktion übergeben, die in dem Code ausgeführt wird, der dieselbe Signatur hat, unabhängig davon, woher er stammt.

0
hinzugefügt

Ja, ein Delegierter kann als eine Schnittstelle mit einer Methode betrachtet werden.

0
hinzugefügt

Es gibt einen kleinen Unterschied, Delegaten können auf die Mitgliedsvariablen von Klassen zugreifen, in denen sie definiert sind. In C# (im Gegensatz zu Java) werden alle inneren Klassen als statisch betrachtet. Wenn Sie eine Schnittstelle zum Verwalten eines Rückrufs verwenden, z. ein ActionListener für eine Schaltfläche. Die implementierende innere Klasse muss (über den Konstruktor) Referenzen auf die Teile der enthaltenden Klasse übergeben, mit denen während des Callbacks möglicherweise interagiert werden muss. Delegierte haben diese Einschränkung nicht und reduzieren daher die Menge an Code, die zum Implementieren des Rückrufs erforderlich ist.

Kürzere, prägnanter Code ist auch ein wertvoller Vorteil.

0
hinzugefügt
Es sind nicht nur Mitgliedsvariablen. Es sind sogar lokale Variablen oder Parameter der Methode, in der ein anonymer Delegat definiert ist.
hinzugefügt der Autor Daniel Earwicker, Quelle
@supercat Korrigieren. Aber warum denken Sie, dass Interfaces "besser funktionieren" als Delegierte? Ein Delegat ist logisch dasselbe wie eine Schnittstelle mit einer einzigen Methode.
hinzugefügt der Autor Daniel Earwicker, Quelle
(1) "Heap-Instanzen" sind in CLR nicht besonders teuer. Siehe slemlegantcode.wordpress. com/2009/03/01/& hellip; (2) Was ist los mit Delegat void MyDelegate (T v) wo T: IFoo, IBar; ?
hinzugefügt der Autor Daniel Earwicker, Quelle
hinzugefügt der Autor Daniel Earwicker, Quelle
@DanielEarwicker: Eine Routine, in der eine Closure definiert ist, definiert eine spezielle "versteckte" Klasse für lokale Variablen, erstellt eine Instanz einer solchen Klasse, wenn diese Variablen in den Bereich eintreten, und verwendet die Felder dieser Klasse anstelle von "normal" lokal Variablen. Szenarien, die Schließungen verwenden, würden mit Schnittstellen besser funktionieren als mit Delegierten.
hinzugefügt der Autor supercat, Quelle
@DanielEarwicker: Mindestens zwei Gründe: (1) Angenommen, eine a-Routine ruft in bestimmten Situationen eine Aktion auf, und in dieser Situation möchte ich, dass sie Foo (x) aufruft mit dem aktuellen Wert der lokalen Variablen x . Das Übergeben einer solchen Anforderung als Delegat erfordert die Erstellung von zwei Heapinstanzen: ein temporäres Klassenobjekt, das x enthalten soll, und ein Delegat, der es aufrufen soll. Das Übergeben der Anforderung als Schnittstellentyp erfordert die Erstellung einer Heapinstanz (eine Implementierung d
hinzugefügt der Autor supercat, Quelle
(2) Schnittstellen können offene generische Methoden haben, Delegaten dagegen nicht. Angenommen, ich habe eine Sammlung von Objekten, die keinen gemeinsamen Basistyp haben, aber alle sind von Typen, die gezwungen sind, IFoo und IBar zu implementieren. Ich möchte in der Lage sein, eine extern gelieferte Routine auszuführen, die einen Parameter verwendet, der auf IFoo und IBar beschränkt ist. Es gibt keine Möglichkeit anzugeben, dass ein Delegat einen Parameter vom Typ T: IFoo, IBar akzeptiert, aber es ist möglich, eine Schnittstellenmethode zu deklarieren, die dies tut.
hinzugefügt der Autor supercat, Quelle
@DanielEarwicker: Das Problem dabei ist, dass die Routine, die den Delegaten erstellt, den Typ T angeben muss. Angenommen, eine Klasse hat eine Sammlung von Objekten, die alle I1 und I2 implementieren, aber sie haben keinen gemeinsamen Basistyp, der beides implementiert; Es möchte eine ForAll -Methode verfügbar machen. Wenn ein solches Verfahren ein Objekt vom Typ interface IActUponConstrained {void Act (T param) akzeptiert, wobei T: I1, I2;} , könnte es Act für jedes Element, ohn
hinzugefügt der Autor supercat, Quelle
@DanielEarwicker: Wenn Reflection nicht verwendet werden soll, ist die generische Schnittstelle erforderlich, da generische Typisierungsparameter sich nur "tiefer" in den Call-Stack bewegen können. Eine Sammlung der abstrakten Klasse ThingImplementing kann Instanzen abgeleiteter Klassen enthalten ThingImplementing .WithType wobei ActualType: I1, I2 . Jede Instanz ist an den generischen Typ gebunden, mit dem sie erstellt wurde. Wenn jedoch anderer Code mit diesem Typparameter aufgerufen wird, muss die Instanz den Aufruf selbst durchfüh
hinzugefügt der Autor supercat, Quelle
... beide Schnittstellen, es gibt keine Möglichkeit, einen Delegaten mit einem Typparameter zu definieren, der auf I1 und I2 beschränkt ist, auf den ein ThingImplementing .WithType konnte einen Foo übergeben, und ein ThingImplementing .WithType konnte einen -Bar übergeben Man könnte jedoch eine Implementierung von IActUponConstrained definieren, sie an beide Objekte übergeben und den ersten Aufruf Act (Foo param) while haben die zw
hinzugefügt der Autor supercat, Quelle
@DanielEarwicker: Genau. Die Möglichkeit, dass der Benutzer eines Schnittstellentyps einen generischen Typparameter bereitstellt, ist anders als alles, was mit .net-Delegaten getan werden kann. Ich bin mir nicht sicher, ob Delegierte im Framework wirklich gebraucht werden, wenn Compiler für generische Interfaces ein paar Dinge tun könnten, die sie für Delegierte tun (zB eine Schnittstelle mit einem Invoke ) Methode mit einer bestimmten Signatur, konstruiere eine Klasse, deren Konstruktor zwei Implementierungen dieser Schnittstelle benötigt, und die den Aufruf durch Aufruf beider
hinzugefügt der Autor supercat, Quelle
... Generika gab es in .net 1.0 nicht, also waren quasi-generische Delegierte eine notwendige Notlösung. Übrigens frage ich mich, warum Compiler-generierte Derivate von Delegate nicht ihre eigenen typspezifischen Methoden Combine und Remove definieren? Solche Methoden können ordnungsgemäß mit kontravarianten Delegattypen arbeiten, während Delegate.Combine nicht möglich ist.
hinzugefügt der Autor supercat, Quelle
Es ist interessant festzustellen, dass Delegierte auf Mitglieder zugreifen können. Eine in die Klasse eingefügte Methode, die übergeben werden soll, kann leicht als Kurier für die resultierenden Daten dienen, aber der direkte Zugriff ist ein viel schnellerer Weg, um darüber zu gehen.
hinzugefügt der Autor Rick Minerich, Quelle

Aus Sicht der Softwaretechnik haben Sie recht, Delegierte ähneln Funktionsschnittstellen insofern, als sie eine Funktionsschnittstelle als Prototyp entwickeln.

Sie können auch auf die gleiche Art verwendet werden: Anstatt eine ganze Klasse zu übergeben, die die von Ihnen benötigte Methode enthält, können Sie nur einen Delegierten übergeben. Dies spart eine Menge Code und erzeugt viel besser lesbaren Code.

Darüber hinaus können sie nun mit dem Aufkommen von Lambda-Ausdrücken auch einfach auf Fliegen definiert werden, was ein großer Bonus ist. Während es möglich ist, Klassen in C# on-the-fly zu bauen, ist es wirklich ein großer Schmerz im Hintern.

Ein Vergleich der beiden ist ein interessantes Konzept. Ich hatte vorher nicht überlegt, wie ähnlich die Ideen von einem Use Case- und Code-Strukturierungs-Standpunkt sind.

0
hinzugefügt

Schnittstellen und Delegierte sind zwei völlig verschiedene Dinge, obwohl ich die Versuchung verstehe, Delegierte in Interface-ähnlichen Begriffen zu beschreiben, um das Verständnis zu erleichtern ... aber die Wahrheit nicht zu kennen, kann zu Verwirrung auf der ganzen Linie führen.

Die Delegierten waren (teilweise) inspiriert, weil die schwarze Kunst der C ++ - Methodenzeiger für bestimmte Zwecke ungeeignet ist. Ein klassisches Beispiel ist die Implementierung eines Message-Passing- oder Event-Handling-Mechanismus. Mit Delegaten können Sie eine Methodensignatur ohne Kenntnisse über die Typen oder Schnittstellen einer Klasse definieren. Ich könnte einen "void eventHandler (Event * e)" -Delegaten definieren und ihn für jede Klasse aufrufen, die ihn implementiert hat.

Für einige Einblicke in dieses klassische Problem, und warum Delegierte wünschenswert sind lesen Sie dies und dann das .

0
hinzugefügt