it-swarm.com.de

Wann und warum sollten Delegierte eingesetzt werden?

Ich bin relativ neu in C # und frage mich , wann ich Delegates angemessen einsetzen soll . Sie sind in der Ereignisdeklaration weit verbreitet, aber wann sollte ich sie in meinem eigenen Code verwenden und warum sind sie nützlich? warum nicht etwas anderes verwenden?

Ich frage mich auch , wann ich Delegierte einsetzen muss und ich habe keine andere Alternative .

Danke für die Hilfe!

EDIT: Ich denke, ich habe eine notwendige Verwendung von Delegierten gefunden hier

334
iChaib

Ich bin mit allem einverstanden, was bereits gesagt wurde, und versuche nur, ein paar andere Worte darauf zu setzen.

Ein Delegat kann als Platzhalter für eine oder mehrere Methoden angesehen werden.

Wenn Sie einen Delegaten definieren, sagen Sie dem Benutzer Ihrer Klasse: " Sie können dem Delegaten eine beliebige Methode zuweisen, die dieser Signatur entspricht, und sie wird jedes Mal aufgerufen, wenn mein Delegat anruft heißt ".

Typische Verwendung sind natürlich Ereignisse. Alle OnEventX delegieren an die vom Benutzer definierten Methoden.

Delegierte sind nützlich, um demBenutzerIhrer Objekte die Möglichkeit zu bieten, ihr Verhalten anzupassen. Die meiste Zeit können Sie andere Wege gehen, um den gleichen Zweck zu erreichen, und ich glaube nicht, dass Sie jemals gezwungen sein können, Delegiertezu erstellen. In manchen Situationen ist es nur der einfachste Weg, die Sache zu erledigen.

266
Benoit Vidis

Ein Delegat ist eine Referenz auf eine Methode. Während Objekte leicht als Parameter in Methoden, Konstruktoren oder was auch immer gesendet werden können, sind Methoden etwas kniffliger. Aber hin und wieder müssen Sie möglicherweise eine Methode als Parameter an eine andere Methode senden, und dann benötigen Sie Delegaten.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyLibrary;

namespace DelegateApp {

  /// <summary>
  /// A class to define a person
  /// </summary>
  public class Person {
    public string Name { get; set; }
    public int Age { get; set; }
  }

  class Program {
    //Our delegate
    public delegate bool FilterDelegate(Person p);

    static void Main(string[] args) {

      //Create 4 Person objects
      Person p1 = new Person() { Name = "John", Age = 41 };
      Person p2 = new Person() { Name = "Jane", Age = 69 };
      Person p3 = new Person() { Name = "Jake", Age = 12 };
      Person p4 = new Person() { Name = "Jessie", Age = 25 };

      //Create a list of Person objects and fill it
      List<Person> people = new List<Person>() { p1, p2, p3, p4 };

      //Invoke DisplayPeople using appropriate delegate
      DisplayPeople("Children:", people, IsChild);
      DisplayPeople("Adults:", people, IsAdult);
      DisplayPeople("Seniors:", people, IsSenior);

      Console.Read();
    }

    /// <summary>
    /// A method to filter out the people you need
    /// </summary>
    /// <param name="people">A list of people</param>
    /// <param name="filter">A filter</param>
    /// <returns>A filtered list</returns>
    static void DisplayPeople(string title, List<Person> people, FilterDelegate filter) {
      Console.WriteLine(title);

      foreach (Person p in people) {
        if (filter(p)) {
          Console.WriteLine("{0}, {1} years old", p.Name, p.Age);
        }
      }

      Console.Write("\n\n");
    }

    //==========FILTERS===================
    static bool IsChild(Person p) {
      return p.Age < 18;
    }

    static bool IsAdult(Person p) {
      return p.Age >= 18;
    }

    static bool IsSenior(Person p) {
      return p.Age >= 65;
    }
  }
}
280
dhaval8087

Angenommen, Sie möchten eine Prozedur schreiben, um eine reelle Funktion f (x) über ein Intervall [a, b] zu integrieren. Nehmen wir an, wir möchten dazu die 3-Punkt-Gauß-Methode verwenden (das kann natürlich jeder).

Idealerweise möchten wir eine Funktion, die so aussieht:

// 'f' is the integrand we want to integrate over [a, b] with 'n' subintervals.
static double Gauss3(Integrand f, double a, double b, int n) {
  double res = 0;

  // compute result
  // ...

  return res;
}

Wir können also jedes Integrand, f übergeben und sein bestimmtes Integral über das geschlossene Intervall erhalten.

Welcher Typ sollte Integrand sein?

Ohne Delegierte

Nun, ohne Delegierte bräuchten wir eine Schnittstelle mit einer einzigen Methode, sagen wir eval, die wie folgt deklariert ist:

// Interface describing real-valued functions of one variable.
interface Integrand {
  double eval(double x);
}

Dann müssten wir eine ganze Reihe von Klassen erstellen, die diese Schnittstelle implementieren:

// Some function
class MyFunc1 : Integrand {
  public double eval(double x) {
    return /* some_result */ ;
  }
}

// Some other function
class MyFunc2 : Integrand {
  public double eval(double x) {
    return /* some_result */ ;
  }
}

// etc

Um sie dann in unserer Gauss3-Methode zu verwenden, müssen wir sie wie folgt aufrufen:

double res1 = Gauss3(new MyFunc1(), -1, 1, 16);
double res2 = Gauss3(new MyFunc2(), 0, Math.PI, 16);

Und Gauß3 muss so aussehen:

static double Gauss3(Integrand f, double a, double b, int n) {
  // Use the integrand passed in:
  f.eval(x);
}

Also müssen wir das alles nur tun, um unsere willkürlichen Funktionen in Guass3 zu verwenden.

Mit Delegierten

public delegate double Integrand(double x);

Nun können wir einige statische (oder nicht statische) Funktionen definieren, die diesem Prototyp entsprechen:

class Program {
   public delegate double Integrand(double x);   
   // Define implementations to above delegate 
   // with similar input and output types
   static double MyFunc1(double x) { /* ... */ }
   static double MyFunc2(double x) { /* ... */ }
   // ... etc ...

   public static double Gauss3(Integrand f, ...) { 
      // Now just call the function naturally, no f.eval() stuff.
      double a = f(x); 
      // ...
   }

   // Let's use it
   static void Main() {
     // Just pass the function in naturally (well, its reference).
     double res = Gauss3(MyFunc1, a, b, n);
     double res = Gauss3(MyFunc2, a, b, n);    
   }
}

Keine Schnittstellen, keine klobigen .eval-Dateien, keine Objektinstanziierung, nur einfache Funktionen, die wie Zeiger funktionieren, für eine einfache Aufgabe.

Natürlich sind Delegierte mehr als nur Funktionszeiger unter der Haube, aber das ist ein separates Thema (Funktionsverkettung und Ereignisse).

143
Alex Budovski

Delegaten sind äußerst nützlich, wenn Sie einen Codeblock deklarieren möchten, den Sie weitergeben möchten. Zum Beispiel bei Verwendung eines generischen Wiederholungsmechanismus.

Pseudo:

function Retry(Delegate func, int numberOfTimes)
    try
    {
       func.Invoke();
    }
    catch { if(numberOfTimes blabla) func.Invoke(); etc. etc. }

Oder wenn Sie eine späte Auswertung von Codeblöcken durchführen möchten, z. B. eine Funktion mit einer Aktion Transform, und eine Aktion BeforeTransform und eine Aktion AfterTransform, die Sie in Ihrer Transformationsfunktion auswerten können, ohne zu wissen, ob die Aktion BeginTransform gefüllt ist , oder was es zu transformieren hat.

Und natürlich beim Erstellen von Event-Handlern. Sie möchten den Code nicht sofort, sondern nur bei Bedarf auswerten, um einen Delegaten zu registrieren, der beim Eintreten des Ereignisses aufgerufen werden kann.

28
Jan Jongboom

Ich habe mich nur darum gekümmert, und daher möchte ich Ihnen ein Beispiel vorstellen, da Sie bereits Beschreibungen haben. Derzeit sehe ich jedoch den Vorteil, dass Sie die Warnungen im Stil "Zirkelreferenz" umgehen, bei denen nicht zwei Projekte aufeinander verweisen können andere.

Angenommen, eine Anwendung lädt eine XML-Datei herunter und speichert sie anschließend in einer Datenbank.

Ich habe hier 2 Projekte, die meine Lösung erstellen: FTP und eine SaveDatabase.

Unsere Anwendung sucht zunächst nach Downloads und lädt die Datei (en) herunter. Anschließend wird das SaveDatabase-Projekt aufgerufen.

Jetzt muss unsere Anwendung die FTP-Site benachrichtigen, wenn eine Datei in der Datenbank gespeichert wird, indem sie eine Datei mit Metadaten hochlädt (ignorieren Sie, warum dies eine Anfrage des Besitzers der FTP-Site ist). Die Frage ist, wann und wie? Wir brauchen eine neue Methode namens NotifyFtpComplete (), aber in welchem ​​unserer Projekte sollte sie auch gespeichert werden - FTP oder SaveDatabase? Logischerweise sollte der Code in unserem FTP-Projekt vorhanden sein. Dies würde jedoch bedeuten, dass unser NotifyFtpComplete ausgelöst werden muss, oder es muss warten, bis der Speichervorgang abgeschlossen ist, und dann die Datenbank abfragen, um sicherzustellen, dass sie sich dort befindet. Wir müssen unser SaveDatabase-Projekt anweisen, die NotifyFtpComplete () -Methode direkt aufzurufen, können dies aber nicht. Wir würden einen Ciruclar-Verweis erhalten und NotifyFtpComplete () ist eine private Methode. Was für eine Schande, das hätte funktioniert. Nun, das kann es.

Im Code unserer Anwendung hätten wir Parameter zwischen Methoden übergeben, aber was wäre, wenn einer dieser Parameter die NotifyFtpComplete-Methode wäre? Ja, wir übergeben die Methode, wobei der gesamte Code ebenfalls enthalten ist. Dies würde bedeuten, dass wir die Methode jederzeit und von jedem Projekt aus ausführen könnten. Nun, das ist es, was der Delegierte ist. Das heißt, wir können die NotifyFtpComplete () -Methode als Parameter an unsere SaveDatabase () -Klasse übergeben. An dem Punkt, an dem es speichert, führt es einfach den Delegaten aus.

Sehen Sie nach, ob dieses grobe Beispiel hilft (Pseudocode). Wir gehen auch davon aus, dass die Anwendung mit der Begin () -Methode der FTP-Klasse beginnt.

class FTP
{
    public void Begin()
    {
        string filePath = DownloadFileFromFtpAndReturnPathName();

        SaveDatabase sd = new SaveDatabase();
        sd.Begin(filePath, NotifyFtpComplete());
    }

    private void NotifyFtpComplete()
    {
        //Code to send file to FTP site
    }
}


class SaveDatabase
{
    private void Begin(string filePath, delegateType NotifyJobComplete())
    {
        SaveToTheDatabase(filePath);

        /* InvokeTheDelegate - 
         * here we can execute the NotifyJobComplete
         * method at our preferred moment in the application,
         * despite the method being private and belonging
         * to a different class.
         */
        NotifyJobComplete.Invoke();
    }
}

Nachdem dies erklärt wurde, können wir es jetzt mit dieser Konsolenanwendung unter Verwendung von C # tun.

using System;

namespace ConsoleApplication1
{
    /* I've made this class private to demonstrate that 
    * the SaveToDatabase cannot have any knowledge of this Program class.
    */
    class Program
    {
        static void Main(string[] args)
        {
            //Note, this NotifyDelegate type is defined in the SaveToDatabase project
            NotifyDelegate nofityDelegate = new NotifyDelegate(NotifyIfComplete);

            SaveToDatabase sd = new SaveToDatabase();            
            sd.Start(nofityDelegate);
            Console.ReadKey();
        }

        /* this is the method which will be delegated -
         * the only thing it has in common with the NofityDelegate
         * is that it takes 0 parameters and that it returns void.
         * However, it is these 2 which are essential.
         * It is really important to notice that it writes
         * a variable which, due to no constructor,
         * has not yet been called (so _notice is not initialized yet).
         */ 
    private static void NotifyIfComplete()
    {
        Console.WriteLine(_notice);
    }

    private static string _notice = "Notified";
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegate nd)
        {
            /* I shouldn't write to the console from here, 
             * just for demonstration purposes
             */
            Console.WriteLine("SaveToDatabase Complete");
            Console.WriteLine(" ");
            nd.Invoke();
        }
    }
    public delegate void NotifyDelegate();
}

Ich schlage vor, Sie gehen den Code durch und sehen, wann _notice aufgerufen wird und wann die Methode (Delegat) so aufgerufen wird. Ich hoffe, dies wird die Dinge sehr klar machen.

Zuletzt können wir es jedoch nützlicher machen, indem wir den Delegattyp so ändern, dass er einen Parameter enthält.

using System.Text;

namespace ConsoleApplication1
{
    /* I've made this class private to demonstrate that the SaveToDatabase
     * cannot have any knowledge of this Program class.
     */
    class Program
    {
        static void Main(string[] args)
        {
            SaveToDatabase sd = new SaveToDatabase();
            /* Please note, that although NotifyIfComplete()
         * takes a string parameter, we do not declare it,
         * all we want to do is tell C# where the method is
         * so it can be referenced later,
         * we will pass the parameter later.
         */
            var notifyDelegateWithMessage = new NotifyDelegateWithMessage(NotifyIfComplete);

            sd.Start(notifyDelegateWithMessage );

            Console.ReadKey();
        }

        private static void NotifyIfComplete(string message)
        {
            Console.WriteLine(message);
        }
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegateWithMessage nd)
        {
                        /* To simulate a saving fail or success, I'm just going
         * to check the current time (well, the seconds) and
         * store the value as variable.
         */
            string message = string.Empty;
            if (DateTime.Now.Second > 30)
                message = "Saved";
            else
                message = "Failed";

            //It is at this point we pass the parameter to our method.
            nd.Invoke(message);
        }
    }

    public delegate void NotifyDelegateWithMessage(string message);
}
21
Dave

Teilnehmerübersicht

Delegierte haben die folgenden Eigenschaften:

  • Delegaten ähneln C++ - Funktionszeigern, sind jedoch typsicher.
  • Delegaten ermöglichen die Übergabe von Methoden als Parameter.
  • Delegaten können zum Definieren von Rückrufmethoden verwendet werden.
  • Delegierte können miteinander verkettet werden. Beispielsweise können mehrere Methoden für ein einziges Ereignis aufgerufen werden.
  • Methoden müssen nicht genau mit der Signatur des Delegaten übereinstimmen. Weitere Informationen finden Sie unter Kovarianz und Kontravarianz.
  • In C # Version 2.0 wird das Konzept der anonymen Methoden eingeführt, mit denen Codeblöcke anstelle einer separat definierten Methode als Parameter übergeben werden können.
21

Ich betrachte Delegierte als anonyme Schnittstellen . In vielen Fällen können Sie sie immer dann verwenden, wenn Sie eine Schnittstelle mit einer einzigen Methode benötigen, aber Sie möchten nicht den Overhead, diese Schnittstelle zu definieren.

9
Mark Seemann

Ein Delegat ist eine einfache Klasse, die verwendet wird, um auf Methoden mit einer bestimmten Signatur zu verweisen, die im Wesentlichen zu einem typsicheren Funktionszeiger werden. Der Zweck eines Delegaten besteht darin, einen strukturierten Rückruf zu einer anderen Methode (oder Methoden) zu ermöglichen, nachdem eine abgeschlossen wurde.

Es ist zwar möglich, einen umfangreichen Satz von Code zu erstellen, um diese Funktionalität auszuführen, dies ist jedoch nicht erforderlich. Sie können einen Delegaten verwenden.

Das Erstellen eines Delegaten ist einfach. Identifizieren Sie die Klasse als Stellvertreter mit dem Schlüsselwort "delegate". Geben Sie dann die Signatur des Typs an.

3
Pankaj