Zuallererst eine gute Frage. Ich bewundere, dass Sie sich auf den Nutzen konzentrieren, anstatt blind "Best Practices" zu akzeptieren. +1 dafür.
Ich habe diesen Leitfaden schon einmal gelesen. Sie müssen sich an etwas erinnern - es ist nur eine Anleitung, hauptsächlich für C # -Neulinge, die wissen, wie man programmiert, aber nicht so vertraut mit der C # -Methode sind. Es ist nicht nur eine Seite mit Regeln, sondern eine Seite, die beschreibt, wie die Dinge normalerweise bereits erledigt werden. Und da sie bereits überall auf diese Weise ausgeführt werden, ist es möglicherweise eine gute Idee, konsequent zu bleiben.
Ich werde auf den Punkt kommen und Ihre Fragen beantworten.
Zunächst gehe ich davon aus, dass Sie bereits wissen, was eine Schnittstelle ist. Für einen Delegaten reicht es zu sagen, dass es sich um eine Struktur handelt, die einen typisierten Zeiger auf eine Methode sowie einen optionalen Zeiger auf das Objekt enthält, das das this
Argument für diese Methode darstellt. Bei statischen Methoden ist der letztere Zeiger null.
Es gibt auch Multicast-Delegaten, die genau wie Delegaten sind, denen jedoch möglicherweise mehrere dieser Strukturen zugewiesen sind (dh ein einzelner Aufruf zum Aufrufen eines Multicast-Delegaten ruft alle Methoden in der ihm zugewiesenen Aufrufliste auf).
Was verstehen sie unter einem ereignisreichen Designmuster?
Dies bedeutet, dass Ereignisse in C # verwendet werden (das über spezielle Schlüsselwörter verfügt, um dieses äußerst nützliche Muster zu implementieren). Ereignisse in C # werden von Multicast-Delegierten unterstützt.
Wenn Sie ein Ereignis definieren, wie in diesem Beispiel:
class MyClass {
// Note: EventHandler is just a multicast delegate,
// that returns void and accepts (object sender, EventArgs e)!
public event EventHandler MyEvent;
public void DoSomethingThatTriggersMyEvent() {
// ... some code
var handler = MyEvent;
if (handler != null)
handler(this, EventArgs.Empty);
// ... some other code
}
}
Der Compiler wandelt dies tatsächlich in den folgenden Code um:
class MyClass {
private EventHandler MyEvent = null;
public void add_MyEvent(EventHandler value) {
MyEvent += value;
}
public void remove_MyEvent(EventHandler value) {
MyEvent -= value;
}
public void DoSomethingThatTriggersMyEvent() {
// ... some code
var handler = MyEvent;
if (handler != null)
handler(this, EventArgs.Empty);
// ... some other code
}
}
Anschließend abonnieren Sie eine Veranstaltung mit
MyClass instance = new MyClass();
instance.MyEvent += SomeMethodInMyClass;
Welches kompiliert bis zu
MyClass instance = new MyClass();
instance.add_MyEvent(new EventHandler(SomeMethodInMyClass));
Das ist Eventing in C # (oder .NET im Allgemeinen).
Wie einfach gestaltet sich die Komposition, wenn ein Delegierter eingesetzt wird?
Dies kann leicht demonstriert werden:
Angenommen, Sie haben eine Klasse, die von einer Reihe von Aktionen abhängt, die an sie übergeben werden sollen. Sie können diese Aktionen in eine Schnittstelle einbetten:
interface RequiredMethods {
void DoX();
int DoY();
};
Und jeder, der Aktionen an Ihre Klasse übergeben wollte, musste diese Schnittstelle zuerst implementieren. Oder Sie könnten ihr Leben leichter machen, indem Sie von der folgenden Klasse abhängen:
sealed class RequiredMethods {
public Action DoX;
public Func<int> DoY();
}
Auf diese Weise müssen die Aufrufer nur eine Instanz von RequiredMethods erstellen und Methoden zur Laufzeit an die Delegaten binden. Dies ist normalerweise einfacher.
Diese Vorgehensweise ist unter den richtigen Umständen äußerst vorteilhaft. Denken Sie darüber nach - warum sollten Sie auf eine Schnittstelle angewiesen sein, wenn Sie sich nur für eine Implementierung interessieren?
Vorteile der Verwendung von Schnittstellen bei einer Gruppe verwandter Methoden
Es ist vorteilhaft, Schnittstellen zu verwenden, da Schnittstellen normalerweise explizite Implementierungen zur Kompilierungszeit erfordern. Dies bedeutet, dass Sie eine neue Klasse erstellen.
Und wenn Sie eine Gruppe verwandter Methoden in einem einzigen Paket haben, ist es vorteilhaft, wenn dieses Paket von anderen Teilen des Codes wiederverwendet werden kann. Wenn sie also einfach eine Klasse instanziieren können, anstatt eine Gruppe von Delegierten zu erstellen, ist das einfacher.
Vorteile der Verwendung von Schnittstellen, wenn eine Klasse nur eine Implementierung benötigt
Wie bereits erwähnt, werden Schnittstellen in der Kompilierungszeit implementiert. Dies bedeutet, dass sie effizienter sind als das Aufrufen eines Delegaten (was per se eine Indirektionsebene darstellt).
"Eine Implementierung" kann eine Implementierung bedeuten, die an einem einzigen genau definierten Ort vorhanden ist.
Andernfalls kann eine Implementierung von einer beliebigen Stelle im Programm kommen, die zufällig der Methodensignatur entspricht. Dies ermöglicht mehr Flexibilität, da Methoden nur der erwarteten Signatur entsprechen müssen, anstatt zu einer Klasse zu gehören, die explizit eine bestimmte Schnittstelle implementiert. Diese Flexibilität kann jedoch mit Kosten verbunden sein und sogar das Liskov-Substitutionsprinzip verletzen , da in den meisten Fällen explizite Aussagen erforderlich sind , da die Wahrscheinlichkeit von Unfällen minimiert wird. Genau wie beim statischen Tippen.
Der Begriff könnte sich hier auch auf Multicast-Delegierte beziehen. Von Interfaces deklarierte Methoden können nur einmal in einer implementierenden Klasse implementiert werden. Delegaten können jedoch mehrere Methoden akkumulieren, die nacheinander aufgerufen werden.
Alles in allem sieht es so aus, als ob der Leitfaden nicht informativ genug ist und lediglich als das fungiert, was er ist - ein Leitfaden, kein Regelwerk. Einige Ratschläge klingen tatsächlich etwas widersprüchlich. Es liegt an Ihnen, zu entscheiden, wann es richtig ist, was anzuwenden. Der Führer scheint uns nur einen allgemeinen Weg zu geben.
Ich hoffe, Ihre Fragen wurden zu Ihrer Zufriedenheit beantwortet. Und wieder ein dickes Lob für die Frage.