Meinen Sie Delegaten im .NET-Typsystem oder die C # -Delegatensyntax? Meinen Sie "wann verwenden Sie die Delegatensyntax anstelle der Lambda-Ausdruckssyntax" oder meinen Sie "wann verwenden Sie Delegaten anstelle von Klassen / Schnittstellen / virtuellen Methoden / etc."?
Jetzt, da wir Lambda-Ausdrücke und anonyme Methoden in C # haben, verwende ich viel mehr Delegaten. In C # 1, wo Sie immer eine separate Methode haben mussten, um die Logik zu implementieren, war die Verwendung eines Delegaten oft nicht sinnvoll. In diesen Tagen benutze ich Delegierte für:
Ereignishandler (für GUI und mehr)
Threads starten
Rückrufe (z. B. für asynchrone APIs)
LINQ und ähnliches (List.Find etc)
Überall sonst, wo ich effektiv "Vorlagen" -Code mit einer speziellen Logik anwenden möchte (wo der Delegat die Spezialisierung bereitstellt)
Ich bin mir nicht sicher, wie ich es kurz erklären würde, ohne die Dinge verwirrender zu machen :) (Vermutlich wird es von Event-Handlern, LINQ und dem Templaty-Bit behandelt!
Jon Skeet
1
Dein erster Satz macht nicht viel Sinn.
Senfo
3
Ich weiß, was Sie sagen wollen, aber ich würde Folgendes leichter lesen können: "Jetzt, da wir Lambda-Ausdrücke und anonyme Methoden in C # haben, verwende ich viel mehr Delegaten." Ich weiß, dass ich nicht picke, aber ich musste diesen Satz wirklich ein paar Mal lesen, bevor er für mich Sinn machte.
Senfo
4
+1 für den Mut, den ehrwürdigen Mr. Skeet herauszufordern ;-)
Indra
28
Delegierte sind für viele Zwecke sehr nützlich.
Ein solcher Zweck besteht darin, sie zum Filtern von Datensequenzen zu verwenden. In diesem Fall würden Sie einen Prädikatdelegaten verwenden, der ein Argument akzeptiert und je nach Implementierung des Delegaten selbst true oder false zurückgibt.
Hier ist ein dummes Beispiel - ich bin sicher, Sie können daraus etwas Nützlicheres extrapolieren:
using System;
using System.Linq;
using System.Collections.Generic;classProgram{staticvoidMain(){List<String> names =newList<String>{"Nicole Hare","Michael Hare","Joe Hare","Sammy Hare","George Washington",};// Here I am passing "inMyFamily" to the "Where" extension method// on my List<String>. The C# compiler automatically creates // a delegate instance for me.IEnumerable<String> myFamily = names.Where(inMyFamily);foreach(String name in myFamily)Console.WriteLine(name);}staticBoolean inMyFamily(String name){return name.EndsWith("Hare");}}
Die static Boolean inMyFamily(String name)Methode ist der Delegat. Wobei ein Delegat als Parameter verwendet wird. Da Delegaten nur Funktionszeiger sind, wenn Sie den Methodennamen an den übergeben .Where(delegate), der zum Delegaten wird. Da inMyFamily einen booleschen Typ zurückgibt, wird er tatsächlich als Prädikat betrachtet. Prädikate sind nur Delegaten, die Boolesche Werte zurückgeben.
Landon Poch
4
"Prädikate sind nur Delegaten, die Boolesche Werte zurückgeben." +1
daehaai
@ LandPoch dieser Kommentar wäre besser in der Antwort platziert worden. Ich als Anfänger konnte nicht erkennen, wo es war. Vielen Dank.
Eakan Gopalakrishnan
@Eakan, ich habe die Hauptfrage nicht wirklich beantwortet (wann würden Sie Delegierte einsetzen), also habe ich sie stattdessen als Kommentar hinterlassen.
Landon Poch
13
Eine weitere interessante Antwort gefunden:
Ein Mitarbeiter hat mir gerade diese Frage gestellt: Was ist der Sinn von Delegierten in .NET? Meine Antwort war sehr kurz und eine, die er online nicht gefunden hatte: die Ausführung einer Methode zu verzögern.
+ 1..hatte nicht so darüber nachgedacht. Guter Punkt
Luke101
12
Sie können Delegaten verwenden, um funktionsbezogene Variablen und Parameter zu deklarieren.
Beispiel
Betrachten Sie das Muster "Ausleihen von Ressourcen". Sie möchten die Erstellung und Bereinigung einer Ressource steuern und gleichzeitig zulassen, dass Client-Code die Ressource dazwischen "ausleiht".
Jede Methode, die dieser Signatur entspricht, kann verwendet werden, um einen Delegaten dieses Typs zu instanziieren. In C # 2.0 kann dies implizit einfach unter Verwendung des Methodennamens sowie unter Verwendung anonymer Methoden erfolgen.
Diese Methode verwendet den Typ als Parameter. Beachten Sie den Aufruf des Delegaten.
publicclassDataProvider{protectedstring _connectionString;publicDataProvider(string psConnectionString ){
_connectionString = psConnectionString;}publicvoidUseReader(string psSELECT,DataReaderUser readerUser ){
using (SqlConnection connection =newSqlConnection( _connectionString ))try{SqlCommand command =newSqlCommand( psSELECT, connection );
connection.Open();SqlDataReader reader = command.ExecuteReader();while( reader.Read())
readerUser( reader );// the delegate is invoked}catch(System.Exception ex ){// handle exceptionthrow ex;}}}
Die Funktion kann mit einer anonymen Methode wie folgt aufgerufen werden. Beachten Sie, dass die anonyme Methode Variablen verwenden kann, die außerhalb von sich selbst deklariert sind . Dies ist äußerst praktisch (obwohl das Beispiel ein wenig erfunden ist).
string sTableName ="test";string sQuery ="SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='"+ sTableName +"'";DataProvider.UseReader( sQuery,delegate(System.Data.IDataReader reader ){Console.WriteLine( sTableName +"."+ reader[0]);});
Delegaten können häufig anstelle einer Schnittstelle mit einer Methode verwendet werden. Ein häufiges Beispiel hierfür ist das Beobachtermuster. In anderen Sprachen können Sie Folgendes definieren, wenn Sie eine Benachrichtigung erhalten möchten, dass etwas passiert ist:
classIObserver{voidNotify(...);}
In C # wird dies häufiger durch Ereignisse ausgedrückt, bei denen der Handler ein Delegat ist, zum Beispiel:
Ein weiterer großartiger Ort, um Delegaten zu verwenden, wenn Sie ein Prädikat an eine Funktion übergeben müssen, z. B. wenn Sie eine Reihe von Elementen aus einer Liste auswählen:
myList.Where(i => i >10);
Das Obige ist ein Beispiel für die Lambda-Syntax, die auch wie folgt geschrieben werden könnte:
myList.Where(delegate(int i){return i >10;});
Ein weiterer Ort, an dem es nützlich sein kann, Delegaten zu verwenden, ist die Registrierung von Factory-Funktionen, zum Beispiel:
Ich komme sehr spät dazu, aber ich hatte heute Probleme, den Zweck der Delegierten herauszufinden, und schrieb zwei einfache Programme, die die gleiche Ausgabe liefern, von der ich denke, dass sie ihren Zweck gut erklärt.
NoDelegates.cs
using System;publicclassTest{publicconstint MAX_VALUE =255;publicconstint MIN_VALUE =10;publicstaticvoid checkInt(int a){Console.Write("checkInt result of {0}: ", a);if(a < MAX_VALUE && a > MIN_VALUE)Console.WriteLine("max and min value is valid");elseConsole.WriteLine("max and min value is not valid");}publicstaticvoid checkMax(int a){Console.Write("checkMax result of {0}: ", a);if(a < MAX_VALUE)Console.WriteLine("max value is valid");elseConsole.WriteLine("max value is not valid");}publicstaticvoid checkMin(int a){Console.Write("checkMin result of {0}: ", a);if(a > MIN_VALUE)Console.WriteLine("min value is valid");elseConsole.WriteLine("min value is not valid");Console.WriteLine("");}}publicclassDriver{publicstaticvoidMain(string[] args){Test.checkInt(1);Test.checkMax(1);Test.checkMin(1);Test.checkInt(10);Test.checkMax(10);Test.checkMin(10);Test.checkInt(20);Test.checkMax(20);Test.checkMin(20);Test.checkInt(30);Test.checkMax(30);Test.checkMin(30);Test.checkInt(254);Test.checkMax(254);Test.checkMin(254);Test.checkInt(255);Test.checkMax(255);Test.checkMin(255);Test.checkInt(256);Test.checkMax(256);Test.checkMin(256);}}
Delegates.cs
using System;publicdelegatevoidValid(int a);publicclassTest{publicconstint MAX_VALUE =255;publicconstint MIN_VALUE =10;publicstaticvoid checkInt(int a){Console.Write("checkInt result of {0}: ", a);if(a < MAX_VALUE && a > MIN_VALUE)Console.WriteLine("max and min value is valid");elseConsole.WriteLine("max and min value is not valid");}publicstaticvoid checkMax(int a){Console.Write("checkMax result of {0}: ", a);if(a < MAX_VALUE)Console.WriteLine("max value is valid");elseConsole.WriteLine("max value is not valid");}publicstaticvoid checkMin(int a){Console.Write("checkMin result of {0}: ", a);if(a > MIN_VALUE)Console.WriteLine("min value is valid");elseConsole.WriteLine("min value is not valid");Console.WriteLine("");}}publicclassDriver{publicstaticvoidMain(string[] args){Valid v1 =newValid(Test.checkInt);
v1 +=newValid(Test.checkMax);
v1 +=newValid(Test.checkMin);
v1(1);
v1(10);
v1(20);
v1(30);
v1(254);
v1(255);
v1(256);}}
Eine etwas andere Verwendung besteht darin, die Reflexion zu beschleunigen. Anstatt jedes Mal Reflektion zu verwenden, können Sie Delegate.CreateDelegateeinen (typisierten) Delegaten für eine Methode (a MethodInfo) erstellen und stattdessen diesen Delegaten aufrufen. Dies ist dann pro Anruf viel schneller, da die Überprüfungen bereits durchgeführt wurden.
Mit Expressionkönnen Sie das Gleiche auch tun, um Code im laufenden Betrieb zu erstellen. Sie können beispielsweise ganz einfach einen ExpressionOperator erstellen , der den Operator + für einen zur Laufzeit ausgewählten Typ darstellt (um Operatorunterstützung für Generika bereitzustellen, die die Sprache nicht bietet). ;; und Sie können einen Expressionan einen typisierten Delegaten kompilierten Job erledigen.
Delegaten werden jedes Mal verwendet, wenn Sie Ereignisse verwenden - das ist der Mechanismus, mit dem sie arbeiten.
Darüber hinaus sind Delegaten sehr nützlich, wenn Sie beispielsweise LINQ-Abfragen verwenden. Beispielsweise benötigen viele LINQ-Abfragen (häufig Func<T,TResult>) einen Delegaten , der zum Filtern verwendet werden kann.
Ein Beispiel könnte wie zu sehen ist hier . Sie haben eine Methode, um ein Objekt zu verarbeiten, das bestimmte Anforderungen erfüllt. Sie möchten das Objekt jedoch auf mehrere Arten verarbeiten können. Anstatt separate Methoden erstellen zu müssen, können Sie einfach eine übereinstimmende Methode zuweisen, die das Objekt einem Delegaten verarbeitet, und den Delegaten an die Methode übergeben, die die Objekte auswählt. Auf diese Weise können Sie der einen Auswahlmethode verschiedene Methoden zuweisen. Ich habe versucht, dies leicht verständlich zu machen.
Ich benutze Delegaten, um mit Threads zu kommunizieren.
Zum Beispiel könnte ich eine Win Forms App haben, die eine Datei herunterlädt. Die App startet einen Worker-Thread, um den Download durchzuführen (wodurch verhindert wird, dass die GUI blockiert). Der Worker-Thread verwendet Delegaten, um Statusmeldungen (z. B. Download-Fortschritt) an das Hauptprogramm zurückzusenden, damit die GUI die Statusleiste aktualisieren kann.
Die erste Verwendungslinie besteht darin, das Observer / Observable-Muster (Ereignisse) zu ersetzen. Die zweite ist eine schöne, elegante Version des Strategiemusters. Verschiedene andere Verwendungen können gesammelt werden, obwohl esoterischer als diese ersten beiden, denke ich.
Jedes Mal, wenn Sie das Verhalten kapseln möchten, es aber auf einheitliche Weise aufrufen möchten. Ereignishandler, Rückruffunktionen usw. Mit Interfaces und Casts können Sie ähnliche Aufgaben ausführen. Manchmal ist das Verhalten jedoch nicht unbedingt an einen Typ oder ein Objekt gebunden . Manchmal haben Sie nur ein Verhalten, das Sie einkapseln müssen.
Lazy Parameter Initialisierung! Neben allen vorherigen Antworten (Strategiemuster, Beobachtermuster usw.) können Sie mit Delegaten die verzögerte Initialisierung von Parametern durchführen. Angenommen, Sie haben eine Funktion Download (), die viel Zeit in Anspruch nimmt und ein bestimmtes DownloadedObject zurückgibt. Dieses Objekt wird abhängig von bestimmten Bedingungen von einem Speicher verwendet. Normalerweise würden Sie:
storage.Store(conditions,Download(item))
Mit Delegaten (genauer gesagt Lambdas) können Sie jedoch Folgendes tun, indem Sie die Signatur des Geschäfts so ändern, dass es eine Bedingung und eine Funktion <Item, DownloadedObject> erhält, und diese wie folgt verwenden:
storage.Store(conditions,(item)=>Download(item))
Daher wertet der Speicher den Delegaten nur bei Bedarf aus und führt den Download abhängig von den Bedingungen aus.
Kleiner Punkt, aber bezüglich "genauer, Lambdas" - Sie könnten dasselbe mit einer anonymen Methode in C # 2.0 tun, obwohl dies ausführlicher wäre: delegate (ItemType item) {[return] Download (item);}
Marc Gravell
Sicher, genau wie LINQ: Lambdas sind nichts anderes als syntaktischer Zucker für Delegierte. Sie haben nur die Delegierten zugänglicher gemacht.
Santiago Palladino
Lambdas sind etwas mehr als nur Delegierte, da sie sowohl in Ausdrucksbäume als auch in Delegierte konvertierbar sind.
Jon Skeet
Nun, Lambdas können auch zu Ausdrücken kompiliert werden, die sich von Delegierten völlig unterscheiden. In Ihrem Beispiel wurde jedoch Func <,> verwendet, das mit einer anon-Methode verwendet werden kann. Das Schreiben von Ausdrücken in C # 2.0 wäre äußerst schmerzhaft.
Soweit ich weiß, können Delegaten in Funktionszeiger konvertiert werden. Dies macht das Leben VIEL einfacher, wenn mit nativem Code zusammengearbeitet wird, der Funktionszeiger benötigt, da diese effektiv objektorientiert sein können, obwohl der ursprüngliche Programmierer dies nicht vorgesehen hat.
Antworten:
Jetzt, da wir Lambda-Ausdrücke und anonyme Methoden in C # haben, verwende ich viel mehr Delegaten. In C # 1, wo Sie immer eine separate Methode haben mussten, um die Logik zu implementieren, war die Verwendung eines Delegaten oft nicht sinnvoll. In diesen Tagen benutze ich Delegierte für:
quelle
Delegierte sind für viele Zwecke sehr nützlich.
Ein solcher Zweck besteht darin, sie zum Filtern von Datensequenzen zu verwenden. In diesem Fall würden Sie einen Prädikatdelegaten verwenden, der ein Argument akzeptiert und je nach Implementierung des Delegaten selbst true oder false zurückgibt.
Hier ist ein dummes Beispiel - ich bin sicher, Sie können daraus etwas Nützlicheres extrapolieren:
quelle
static Boolean inMyFamily(String name)
Methode ist der Delegat. Wobei ein Delegat als Parameter verwendet wird. Da Delegaten nur Funktionszeiger sind, wenn Sie den Methodennamen an den übergeben.Where(delegate)
, der zum Delegaten wird. Da inMyFamily einen booleschen Typ zurückgibt, wird er tatsächlich als Prädikat betrachtet. Prädikate sind nur Delegaten, die Boolesche Werte zurückgeben.Eine weitere interessante Antwort gefunden:
Quelle: LosTechies
Genau wie LINQ.
quelle
Sie können Delegaten verwenden, um funktionsbezogene Variablen und Parameter zu deklarieren.
Beispiel
Betrachten Sie das Muster "Ausleihen von Ressourcen". Sie möchten die Erstellung und Bereinigung einer Ressource steuern und gleichzeitig zulassen, dass Client-Code die Ressource dazwischen "ausleiht".
Dies deklariert einen Delegatentyp.
Jede Methode, die dieser Signatur entspricht, kann verwendet werden, um einen Delegaten dieses Typs zu instanziieren. In C # 2.0 kann dies implizit einfach unter Verwendung des Methodennamens sowie unter Verwendung anonymer Methoden erfolgen.
Diese Methode verwendet den Typ als Parameter. Beachten Sie den Aufruf des Delegaten.
Die Funktion kann mit einer anonymen Methode wie folgt aufgerufen werden. Beachten Sie, dass die anonyme Methode Variablen verwenden kann, die außerhalb von sich selbst deklariert sind . Dies ist äußerst praktisch (obwohl das Beispiel ein wenig erfunden ist).
quelle
Delegaten können häufig anstelle einer Schnittstelle mit einer Methode verwendet werden. Ein häufiges Beispiel hierfür ist das Beobachtermuster. In anderen Sprachen können Sie Folgendes definieren, wenn Sie eine Benachrichtigung erhalten möchten, dass etwas passiert ist:
In C # wird dies häufiger durch Ereignisse ausgedrückt, bei denen der Handler ein Delegat ist, zum Beispiel:
Ein weiterer großartiger Ort, um Delegaten zu verwenden, wenn Sie ein Prädikat an eine Funktion übergeben müssen, z. B. wenn Sie eine Reihe von Elementen aus einer Liste auswählen:
Das Obige ist ein Beispiel für die Lambda-Syntax, die auch wie folgt geschrieben werden könnte:
Ein weiterer Ort, an dem es nützlich sein kann, Delegaten zu verwenden, ist die Registrierung von Factory-Funktionen, zum Beispiel:
Ich hoffe das hilft!
quelle
Ich komme sehr spät dazu, aber ich hatte heute Probleme, den Zweck der Delegierten herauszufinden, und schrieb zwei einfache Programme, die die gleiche Ausgabe liefern, von der ich denke, dass sie ihren Zweck gut erklärt.
NoDelegates.cs
Delegates.cs
quelle
Eine etwas andere Verwendung besteht darin, die Reflexion zu beschleunigen. Anstatt jedes Mal Reflektion zu verwenden, können Sie
Delegate.CreateDelegate
einen (typisierten) Delegaten für eine Methode (aMethodInfo
) erstellen und stattdessen diesen Delegaten aufrufen. Dies ist dann pro Anruf viel schneller, da die Überprüfungen bereits durchgeführt wurden.Mit
Expression
können Sie das Gleiche auch tun, um Code im laufenden Betrieb zu erstellen. Sie können beispielsweise ganz einfach einenExpression
Operator erstellen , der den Operator + für einen zur Laufzeit ausgewählten Typ darstellt (um Operatorunterstützung für Generika bereitzustellen, die die Sprache nicht bietet). ;; und Sie können einenExpression
an einen typisierten Delegaten kompilierten Job erledigen.quelle
Delegaten werden jedes Mal verwendet, wenn Sie Ereignisse verwenden - das ist der Mechanismus, mit dem sie arbeiten.
Darüber hinaus sind Delegaten sehr nützlich, wenn Sie beispielsweise LINQ-Abfragen verwenden. Beispielsweise benötigen viele LINQ-Abfragen (häufig
Func<T,TResult>
) einen Delegaten , der zum Filtern verwendet werden kann.quelle
Eventhandler für Events abonnieren
quelle
Ein Beispiel könnte wie zu sehen ist hier . Sie haben eine Methode, um ein Objekt zu verarbeiten, das bestimmte Anforderungen erfüllt. Sie möchten das Objekt jedoch auf mehrere Arten verarbeiten können. Anstatt separate Methoden erstellen zu müssen, können Sie einfach eine übereinstimmende Methode zuweisen, die das Objekt einem Delegaten verarbeitet, und den Delegaten an die Methode übergeben, die die Objekte auswählt. Auf diese Weise können Sie der einen Auswahlmethode verschiedene Methoden zuweisen. Ich habe versucht, dies leicht verständlich zu machen.
quelle
Ich benutze Delegaten, um mit Threads zu kommunizieren.
Zum Beispiel könnte ich eine Win Forms App haben, die eine Datei herunterlädt. Die App startet einen Worker-Thread, um den Download durchzuführen (wodurch verhindert wird, dass die GUI blockiert). Der Worker-Thread verwendet Delegaten, um Statusmeldungen (z. B. Download-Fortschritt) an das Hauptprogramm zurückzusenden, damit die GUI die Statusleiste aktualisieren kann.
quelle
Für Event-Handler
Methode in Methodenparametern übergeben
quelle
Die erste Verwendungslinie besteht darin, das Observer / Observable-Muster (Ereignisse) zu ersetzen. Die zweite ist eine schöne, elegante Version des Strategiemusters. Verschiedene andere Verwendungen können gesammelt werden, obwohl esoterischer als diese ersten beiden, denke ich.
quelle
Ereignisse, andere beliebige Operationen
quelle
Jedes Mal, wenn Sie das Verhalten kapseln möchten, es aber auf einheitliche Weise aufrufen möchten. Ereignishandler, Rückruffunktionen usw. Mit Interfaces und Casts können Sie ähnliche Aufgaben ausführen. Manchmal ist das Verhalten jedoch nicht unbedingt an einen Typ oder ein Objekt gebunden . Manchmal haben Sie nur ein Verhalten, das Sie einkapseln müssen.
quelle
Lazy Parameter Initialisierung! Neben allen vorherigen Antworten (Strategiemuster, Beobachtermuster usw.) können Sie mit Delegaten die verzögerte Initialisierung von Parametern durchführen. Angenommen, Sie haben eine Funktion Download (), die viel Zeit in Anspruch nimmt und ein bestimmtes DownloadedObject zurückgibt. Dieses Objekt wird abhängig von bestimmten Bedingungen von einem Speicher verwendet. Normalerweise würden Sie:
Mit Delegaten (genauer gesagt Lambdas) können Sie jedoch Folgendes tun, indem Sie die Signatur des Geschäfts so ändern, dass es eine Bedingung und eine Funktion <Item, DownloadedObject> erhält, und diese wie folgt verwenden:
Daher wertet der Speicher den Delegaten nur bei Bedarf aus und führt den Download abhängig von den Bedingungen aus.
quelle
Verwendung von Delegierten
quelle
Der Vergleichsparameter in In Array.Sort (Array T [], Vergleichsvergleich), List.Sort (Vergleichsvergleich) usw.
quelle
Soweit ich weiß, können Delegaten in Funktionszeiger konvertiert werden. Dies macht das Leben VIEL einfacher, wenn mit nativem Code zusammengearbeitet wird, der Funktionszeiger benötigt, da diese effektiv objektorientiert sein können, obwohl der ursprüngliche Programmierer dies nicht vorgesehen hat.
quelle
Delegaten werden verwendet, um eine Methode anhand ihrer Referenz aufzurufen. Beispielsweise:
quelle