Wann würden Sie Delegaten in C # verwenden? [geschlossen]

101

Was verwenden Sie Delegaten in C #?

Maxime Rouiller
quelle
2
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."?
Niki

Antworten:

100

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)
Jon Skeet
quelle
Erwähnenswert ist der "Push" in Push LINQ?
Marc Gravell
3
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;

class Program
{
    static void Main()
    {
        List<String> names = new List<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);
    }

    static Boolean inMyFamily(String name)
    {
        return name.EndsWith("Hare");
    }
}
Andrew Hare
quelle
11
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.

Quelle: LosTechies

Genau wie LINQ.

Maxime Rouiller
quelle
+ 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".

Dies deklariert einen Delegatentyp.

public delegate void DataReaderUser( System.Data.IDataReader dataReader );

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.

public class DataProvider
{
    protected string _connectionString;

    public DataProvider( string psConnectionString )
    {
        _connectionString = psConnectionString;
    }

    public void UseReader( string psSELECT, DataReaderUser readerUser )
    {
        using ( SqlConnection connection = new SqlConnection( _connectionString ) )
        try
        {
            SqlCommand command = new SqlCommand( psSELECT, connection );
            connection.Open();
            SqlDataReader reader = command.ExecuteReader();

            while ( reader.Read() )
                readerUser( reader );  // the delegate is invoked
        }
        catch ( System.Exception ex )
        {
            // handle exception
            throw 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] );
    } );
Harpo
quelle
10

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:

class IObserver{ void Notify(...); }

In C # wird dies häufiger durch Ereignisse ausgedrückt, bei denen der Handler ein Delegat ist, zum Beispiel:

myObject.SomeEvent += delegate{ Console.WriteLine("..."); };

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:

myFactory.RegisterFactory(Widgets.Foo, () => new FooWidget());
var widget = myFactory.BuildWidget(Widgets.Foo);

Ich hoffe das hilft!

jonnii
quelle
Schöne Beispiele mit Syntax .. Danke .. :)
Raghu
10

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;

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void 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");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(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;

public delegate void Valid(int a);

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void 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");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(string [] args) {
        Valid v1 = new Valid(Test.checkInt);
        v1 += new Valid(Test.checkMax);
        v1 += new Valid(Test.checkMin);
        v1(1);
        v1(10);
        v1(20);
        v1(30);
        v1(254);
        v1(255);
        v1(256);
    }
}
werden
quelle
5

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.

Marc Gravell
quelle
5

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.

Reed Copsey
quelle
4

Eventhandler für Events abonnieren

Manu
quelle
2

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.

rookie1024
quelle
1

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.

Danny Frencham
quelle
0
  1. Für Event-Handler

  2. Methode in Methodenparametern übergeben

Patrick Desjardins
quelle
0

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.

x0n
quelle
0

Ereignisse, andere beliebige Operationen


quelle
0

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.

Bob King
quelle
0

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.

Santiago Palladino
quelle
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.
Marc Gravell
0

Verwendung von Delegierten

  1. Handhabung des Events
  2. Multi Casting
Rajeshwaran SP
quelle
0

Der Vergleichsparameter in In Array.Sort (Array T [], Vergleichsvergleich), List.Sort (Vergleichsvergleich) usw.

GregUzelac
quelle
0

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.

Hündchen
quelle
0

Delegaten werden verwendet, um eine Methode anhand ihrer Referenz aufzurufen. Beispielsweise:

  delegate void del_(int no1,int no2);
class Math
{
   public static void add(int x,int y)
   {
     Console.WriteLine(x+y);
   }
   public static void sub(int x,int y)
   {
     Console.WriteLine(x-y);
   }
}



    class Program
    {
        static void Main(string[] args)
        {
            del_ d1 = new del_(Math.add);
            d1(10, 20);
            del_ d2 = new del_(Math.sub);
            d2(20, 10);
            Console.ReadKey();
        }
    }
Mahesh
quelle