Einsatz von C # -Delegierten in der Praxis [geschlossen]

16

Ich denke, dass ich C # -Delegierte konzeptionell verstehe, aber ich habe Mühe, ein reales Beispiel zu finden, in dem sie nützlich wären. Können Sie einige Antworten geben, in denen detailliert beschrieben wird, wie C # -Delegierte in realen Anwendungen verwendet wurden und welche Probleme Sie damit umgehen konnten?

AlexC
quelle
2
Nahezu jede einzelne Klasse in .NET Framework stellt eine Reihe von Ereignissen bereit. Es ist nur eine Möglichkeit, eine Arbeitseinheit zu kapseln. Angenommen, Sie implementieren eine generische binäre Baumstruktur in C. Nun, die einzige Möglichkeit, Ihren Baum zu ordnen, besteht darin, als Parameter einen Funktionszeiger zu verwenden, der wusste, wie die Sortierung durchgeführt wird.
Ed S.

Antworten:

16

GUI-Code verwendet Delegaten, um Ereignisse wie Tastenklicks und Fensterverschiebungen zu verarbeiten. Die Verwendung des Delegaten ermöglicht es Ihnen, eine Funktion aufzurufen, wenn das Ereignis eintritt. Ein Beispiel wäre die Verknüpfung einer Funktion, die Daten speichert, mit einer Schaltfläche "Speichern" auf der Oberfläche. Wenn Sie auf die Schaltfläche klicken, wird die Funktion zum Speichern von Daten ausgeführt. Dies ist bei der GUI-Programmierung hilfreich, da Ihr gesamtes Programm möglicherweise darauf wartet, dass der Benutzer etwas unternimmt, und Sie nicht wissen können, was er zuerst tun wird. Durch die Verwendung von Delegaten kann die Funktionalität Ihres Programms so mit der Benutzeroberfläche verbunden werden, dass der Benutzer die gewünschten Aktionen ausführen kann.

FrustratedWithFormsDesigner
quelle
2
++ Du hast recht, aber ich hasse es immer noch :-) Vor so langer Zeit bin ich stattdessen auf diese Idee gekommen .
Mike Dunlavey
12

Linq verwendet das Func<T>und Action<T>überall als Parameter.

Mit diesen können Sie Lambda-Ausdrücke als Parameter verwenden und die Aktion definieren, die als Teil der Parameterliste ausgeführt werden soll.

Oded
quelle
12

Praktisch alles, was das Beobachtermuster verwendet, würde wahrscheinlich Delegierte implementieren.

Lesen Sie die Beschreibung und stellen Sie sich wahrscheinlich einige Szenarien vor, in denen Sie sie verwenden würden. GUI-Ereignisbehandlung ist ein häufiges Beispiel.

Whatsisname
quelle
+1, Das Strategiemuster ist wirklich der Ort, an dem die Delegierten glänzen, dh Sie haben eine Klasse, in der eine Methode etwas tut, aber Sie möchten, dass das Etwas austauschbar ist und keine direkte Abhängigkeit besteht, also Delegierte. Beachten Sie, dass Ereignisse den gleichen Bedarf wie Delegaten erfüllen. Der Unterschied besteht darin, dass Sie Delegaten verwenden, wenn Sie auf einen bestimmten Rückgabewert reagieren müssen, während Sie nur Ereignisse auslösen und was auch immer sein wird.
Homde
9

Die Delegierten sind bei der asynchronen Programmierung von verdammtem Nutzen.

Sie haben eine Klasse, die asynchron arbeitet und einen Rückruf hat. Sie können die Delegate-Methode beim Rückruf aufrufen lassen - und Ihre Klassenimplementierung führt die in Ihrer Delegate-Methode beschriebene Logik aus.

James Love
quelle
9

Delegierte sind besonders nützlich als Lösung für das Loch im mittleren Muster . Grundsätzlich gibt es viele Fälle, in denen Sie einen eindeutigen Befehlssatz in einen gemeinsamen Befehlssatz einschließen möchten. Es ist besonders schwierig, wenn die Anweisungen vor und nach dem eindeutigen Bit den Status teilen müssen. Bei Delegierten können Sie einen Delegierten einfach an eine Funktion übergeben. Die Funktion führt das Vorher-Bit aus, führt den Delegaten aus und führt dann das Nachher-Bit aus.

Scott Whitlock
quelle
5

In den "alten Tagen" von Nicht-OOP-Sprachen wie Fortran und C war es unglaublich nützlich, dass ein Unterprogramm ein Argument erhielt, das auf eine Funktion zeigte. Beispielsweise arbeitet die qsortFunktion mit einer vom Benutzer bereitgestellten Vergleichsfunktion. Es gibt zahlreiche Unterprogramme zum Lösen gewöhnlicher Differentialgleichungen oder zum Optimieren von Funktionen, und alle verwenden Funktionszeiger als Argumente.

In Fenstersystemen folgen alle Arten von Rückrufen demselben Muster.

In Lisp gab es schon in den Anfängen ein sogenanntes "funktionales Argument" oder FUNARG, das nicht nur eine Funktion war, sondern auch einen Speicherkontext enthielt, in dem es sich an einen Teil der Außenwelt erinnern und mit diesen interagieren konnte.

Dieselbe Anforderung besteht in OOP-Sprachen, außer wenn Sie die Adresse einer Funktion übergeben, müssen Sie auch die Adresse des Objekts übergeben, für das die Funktion eine Methode ist. Das sind zwei Dinge, die man bestehen muss. Ein Delegierter ist genau das und lässt zu, dass dieses gute alte Muster weiterhin verwendet wird.

Mike Dunlavey
quelle
3

Hier ist ein einfaches Beispiel, das zeigt, wie nützlich Delegaten beim Erstellen von einfachem Code sein können, der dem DRY-Prinzip folgt. Sie können den Code auch extrem nahe an der Stelle speichern, an der er benötigt wird.

Action<Button, Action<Button>> prepareButton = 
    (btn, nxt) => { 
        btn.Height = 32;
        btn.Width= 64;
        nxt(btn);
    };

prepareButton(myBtn1, btn => btn.Text = "A");
prepareButton(myBtn2, btn => btn.Text = "B");
prepareButton(myBtn3, btn => btn.Text = "C");

Hier ist ein reales Beispiel für den Vorteil, den Delegierte bieten.

protected override void PageInitialize()
{
    const string selectCodeFormat = "javascript:selectCode('{0}', '{1}');";
    const string onClick = "return toggleElement(this);";

    Func<HtmlGenericControl> getElement = null;
    Action<HtmlGenericControl> setElement = null, addChild = null;
    HtmlGenericControl level1Element = null, level2Element = null, level3Element = null, level4Element = null;
    string className = null, code = null, description = null;           

    using (var records = Core.Database.ExecuteRecords("code.SocCodeTree"))
    {
        while (records.Read())
        {
            code = records.GetString("Code");
            description = records.GetString("Description"); 

            if (records.GetString("Level4") != "")
            {
                className = "Level4";
                setElement = e => level4Element = e;
                getElement = () => level4Element;
                addChild = e => level3Element.Controls.Add(e);
            }
            else if (records.GetString("Level3") != "")
            {
                className = "Level3";
                setElement = e => level3Element = e;
                getElement = () => level3Element;
                addChild = e => level2Element.Controls.Add(e);
            }
            else if (records.GetString("Level2") != "")
            {
                className = "Level2";
                setElement = e => level2Element = e;
                getElement = () => level2Element;
                addChild = e => level1Element.Controls.Add(e);
            }
            else
            {
                className = "Level1";
                setElement = e => level1Element = e;
                getElement = () => level1Element;
                addChild = e => Root.Controls.Add(e);
            }

            var child = new HtmlGenericControl("li");
            child.Attributes["class"] = className;
            var span = new HtmlGenericControl("span") { 
                InnerText = code + " - " + description + " - " 
            };
            span.Attributes["onclick"] = onClick;
            child.Controls.Add(span);
            var a = new HtmlAnchor() { 
                InnerText = "Select", 
                HRef = string.Format(selectCodeFormat, code, description) 
            };
            child.Controls.Add(a);
            setElement(new HtmlGenericControl("ul"));
            child.Controls.Add(getElement());
            addChild(child);    
        }
    }
}
ChaosPandion
quelle
2

Meine erste Begegnung mit Delegierten bestand darin, durch Herunterladen einer Datei von meiner Website nach Programmaktualisierungen zu suchen (Windows Forms C # 3.5). Um jedoch zu vermeiden, dass die Überprüfung des Updates das gesamte Programm sperrte, verwendete ich einen Delegaten und einen Thread, um dies asynchron zu tun.

Ken
quelle
1

Ich habe interessante Implementierungen des Strategiemusters gesehen, bei denen die Delegierten effektiv eingesetzt werden. (dh die Strategie ist ein Delegierter)

Die eine, die ich suchte, war für die Pfadfindung, wo der Algorithmus zum Finden des Pfades ein Delegat war, dem zur Laufzeit (neu) zugewiesen werden konnte, so dass verschiedene Algorithmen verwendet werden konnten (BFS vs A * usw.).

Steven Evers
quelle
1

Viele der klassischen GoF-Muster können mit Delegierten implementiert werden: Beispielsweise können Befehlsmuster, Besuchermuster, Strategiemuster, Factory-Muster und Beobachtermuster häufig mit einem einfachen Delegierten implementiert werden. Manchmal ist eine Klasse besser (z. B. wenn ein Befehl einen Namen benötigt oder ein Strategieobjekt serialisiert werden muss), aber in den meisten Fällen ist die Verwendung von Action<...>oder Func<...>viel eleganter als die Erstellung einer dedizierten Ein-Methoden-Schnittstelle.

Nikie
quelle