Delegaci w C#

Mam problem ze zrozumieniem jak działają delegaty w C#. Mam wiele przykładów kodu, ale nadal nie mogłem go dobrze zrozumieć.

Czy ktoś może mi to wyjaśnić po angielsku? Oczywiście! przykłady kodu pomogą, ale myślę, że potrzebuję więcej opisu jak/dlaczego to działa.

EDIT:

Cóż, pytanie brzmi: dlaczego delegaci działają? Co to jest "schemat" całego procesu?

Jakie są warunki wstępne korzystania z delegatów?

I mam nadzieję, że to wyjaśni pytanie.

Author: Vsevolod Goloviznin, 2009-11-14

10 answers

Jednym ze sposobów myślenia o delegacie jest odwołanie się do funkcji . Powiedzmy na przykład, że masz przycisk w oknie i chcesz, aby coś się stało po kliknięciu przycisku. Możesz dołączyć delegata do zdarzenia kliknięcia przycisku, a za każdym razem, gdy użytkownik kliknie ten przycisk, twoja funkcja zostanie wykonana.

class MyWindow : Window
{
    Button _button;

    public MyWindow()
    {
        _button = new Button();
        // place the button in the window
        _button.Click += MyWindow.ButtonClicked;
    }

    static void ButtonClicked(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Button Clicked");
    }
}

Zauważ, jak robię ButtonClicked funkcję statyczną - chcę zwrócić uwagę na funkcje niestatyczne dalej. Załóżmy, że ButtonClicked jest członek niestatyczny:

class MyWindow : Window
{
    Button _button;
    int _numClicked = 0;

    public MyWindow()
    {
        this._button = new Button();
        // place the button in the window
        this._button.Click += this.ButtonClicked;
    }

    void ButtonClicked(object sender, RoutedEventArgs e)
    {
        this._numClicked += 1;
        MessageBox.Show("Button Clicked " + this._numClicked + " times");
    }
}

Teraz delegat zawiera zarówno odniesienie do funkcji "ButtonClicked", jak i instancję "this", na której metoda jest wywoływana. Instancja "this" w konstruktorze MyWindow i "this" w ButtonClicked są takie same.

Jest to szczególny przypadek koncepcji znanej jako closures, która umożliwia "zapisanie" stanu - bieżącego obiektu, zmiennych lokalnych itp. - podczas tworzenia delegata. W powyższym przykładzie użyliśmy" this " z konstruktora w delegacie. Możemy zrobić więcej:

class MyWindow : Window
{
    Button _button;
    int _numClicked = 0;

    public MyWindow(string localStringParam)
    {
        string localStringVar = "a local variable";
        this._button = new Button();
        // place the button in the window
        this._button.Click += new RoutedEventHandler(
            delegate(object sender, RoutedEventArgs args)
            {
                this._numClicked += 1;
                MessageBox.Show("Param was: " + localStringParam + 
                     " and local var " + localStringVar +
                     " button clicked " + this._numClicked + " times");
            });
    }
}

Tutaj stworzyliśmy anonimowego delegata - funkcję, której nie podano jawnej nazwy. Jedynym sposobem odwoływania się do tej funkcji jest użycie obiektu delegata RoutedEventHandler. Ponadto, funkcja ta istnieje w zakresie konstruktora MyWindow, więc może uzyskać dostęp do wszystkich lokalnych parametrów, zmiennych I instancji członka "this". Będzie nadal przechowywać odwołania do lokalnych zmiennych i parametrów nawet po Konstruktor MyWindow wychodzi.

Na marginesie, delegat będzie również posiadał odniesienie do instancji obiektu - "this" - nawet po usunięciu wszystkich innych odniesień do klasy A. Dlatego, aby zapewnić, że klasa zostanie usunięta, wszyscy delegaci do niestatycznej metody członkowskiej (lub delegaci utworzeni w zakresie jednej z nich) powinni zostać usunięci.

 23
Author: Anton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-14 19:19:07

Delegat to typ. Zmienne typu delegata mogą odwoływać się lub wskazywać na funkcję.

Daje to pośredni sposób wywołania metody, więc metody mogą być wybierane w czasie wykonywania. W ten sposób możesz mieć zmienne, parametry i właściwości zawierające metodę. Właściwości te nazywane są zdarzeniami.

Jeszcze jedna próbka kodu, do uzupełnienia:

   delegate void ADelegate();  // the delegate type

   void Foo() { ... }   // a compatible method
   void Bar() { ... }   // a compatible method

   void Main()
   {
      ADelegate funcPtr;  // a delegate variable

      if (aCondition)
        funcPtr = Foo;  // note: _not_ Foo(), Foo is not executed here
      else
        funcPtr = Bar;

      funcPtr(); // calls Foo or Bar depending on aCondition
   }

Użycie zmiennych delegatów nie jest powszechne. Ale możesz użyć parametru delegate na przykład do sortowania metoda wyboru sortowania rosnąco lub malejąco.

  delegate int Compare(MyClass a, MyClass b);  // the delegate type

  void int CompareUp(MyClass a, MyClass b) { ... }   
  void int CompareDn(MyClass a, MyClass b) { ... }   

  void Sort(MyClass[] data, Compare comparer) { ... }

I prawdopodobnie znasz zdarzenia, które są (specjalnym rodzajem) właściwościami bazującymi na delegatach.

 6
Author: Henk Holterman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-14 19:10:26

Delegaty są wskaźnikami funkcji do metody zdefiniowanej gdzieś.

Załóżmy, że masz klasę BankAccount i musisz wysłać e-mail do klienta, gdy jego saldo jest mniejsze niż $100. Następnie naturalną tendencją jest dodanie czeku w ustawieniu właściwości salda, aby sprawdzić, czy saldo klienta jest mniejsze niż $100, a jeśli tak, Uruchom wiadomość e-mail. Ale ten projekt nie jest elastyczny.

Wady powyższego podejścia:

In the future, there na pewno będzie wymóg, aby wysłać wiadomość tekstową zamiast e-mail do klienta. Niewielu klientów wybiera zarówno e-mail, jak i wiadomości tekstowe. Więc za każdym razem, gdy musisz powiadomić klienta o wszelkich zmianach, pójdziesz i zmodyfikuj klasę BankAccount . Jest to naruszenie otwartych dla rozszerzenia i zamkniętych dla modyfikacji stałych zasad projektowania.

Alternatywne rozwiązanie przy użyciu delegatów:

  1. Zdefiniować metodę NotifyCustomer () , która zajmuje się wysyłaniem powiadomień do Klienta o niskim saldzie, poza klasą BankAccount .

  2. Zmodyfikuj klasę BankAccount , aby zdefiniować delegata i zaakceptować jego wartość w konstruktorach.

  3. Podczas tworzenia klasy BankAccount należy zastosować metodę NotifyCustomer () utworzoną w kroku 1.

  4. W ustawieniu salda klasy BankAccount sprawdź, czy saldo jest mniejsze niż 100 USD. Jeśli tak, wywołaj delegat.

  5. Metoda Notificycustomer () zdefiniowana poza klasą BankAccount zostanie wywołana, co spowoduje wysłanie powiadomienia zgodnie z definicją.

W przyszłości, Jeśli pojawi się nowy sposób powiadamiania klienta, to nie są wymagane żadne zmiany w klasie BankAccount .

Zalety projektowania przy użyciu delegatów:

  1. LOOSE COUPLING : The BankAccount class doesn ' t know about the hard-coded logic powiadamianie klienta.

  2. Zgodnie z zasadą OPEN FOR EXTENSION I CLOSED FOR MODIFICATION : ilekroć medium powiadamiania klienta o zmianach, nie musisz zmieniać klasy BankAccount . Więc teraz z dumą powiesz, że twoja klasa BankAccount działa zgodnie z zasadą projektowania.

Jeśli chcesz dowiedzieć się więcej o delegat in Z przykładem, przeczytaj czym są delegaci i dlaczego potrzebujemy oni.

 4
Author: Ranganatha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-30 15:53:25

To zasada inwersji. Zwykle piszesz kod, który wywołuje metodę, a wywołana metoda jest znana w momencie pisania kodu. Delegaci umożliwiają anonimowe wywoływanie metod. Oznacza to, że nie znasz rzeczywistej metody, która jest wywoływana, gdy program jest uruchomiony.

Jest to przydatne w oddzielaniu obaw różnych części aplikacji. Więc możesz mieć jakiś kod, który wykonuje zadania w magazynie danych. Możesz mieć inny kod, który przetwarza dane. Na procesy na danych nie muszą znać struktury przechowywania danych i przechowywanie danych nie powinno być zależne od wykorzystania danych.

Kod przetwarzania można zapisać zakładając pewne rzeczy dotyczące danych, które są niezależne od struktury magazynu danych. W ten sposób możemy zmienić strukturę magazynu danych, nie martwiąc się o wpływ procesów na danych.

 2
Author: Vincent Ramdhanie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-14 19:06:25

Możesz myśleć o delegatach jako o sposobie wyświetlania kodu jako danych. Jeśli utworzysz delegata, będzie to typ. Zmienne tego typu mogą wskazywać na konkretne metody (zgodne z definicją delegata).

Oznacza to, że można traktować fragment kodu jako dane i na przykład przekazać go do metody. Ponieważ delegaty wskazują na kod (lub null), możesz również wywołać kod, który wskazuje na zmienną.

To pozwala na kilka bardzo przydatnych wzorców. Klasycznym przykładem jest jak Uporządkuj kolekcję. Pozwalając wywołującemu dostarczyć delegata, który implementuje co to znaczy sortować określone elementy, metoda sortowania nie musi nic o tym wiedzieć.

Ten sam pomysł jest szeroko stosowany w wielu metodach LINQ. Tzn. przekazujesz delegata (lub częściej lambda), który obsługuje jakieś konkretne zadanie, a metoda LINQ, o której mowa, wywoła to w celu wykonania zadania.

 2
Author: Brian Rasmussen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-14 19:21:35

Delegat jest typem referencji, który wywołuje jedną / wiele metod za pośrednictwem instancji delegata. Zawiera odniesienie do metod.Delegaty mogą być używane do obsługi (wywoływania/wywoływania) wielu metod w jednym zdarzeniu. Delegaty mogą być używane do definiowania metod asynchronicznych. Oto przykład dla delegata Najpierw tworzymy class.In który ogłaszamy delegatem.i tworzymy metodę wewnątrz klasy, w której wywołujemy delegata.

public class simpleinterest
{
    public delegate void intcal(double i);  //declare delegate
    public event intcal interest; //create delegate object
    public void calculate(double p, double n,double r)
    {
       interest(p*n*r/100);   //invoke delegate
    }

}

Wewnątrz naszego programu wykonujemy mapowanie.Oznacza to, że określamy, które zdarzenie zostanie wywołane podczas wywoływania delegata.

    private void btn_Click(object sender, RoutedEventArgs e)
    {
        simpleinterest s1 = new simpleinterest();
        s1.interest+=new simpleinterest.intcal(s1_interest);//mapping

        s1.calculate(1000,3,10);

    }
    void s1_interest(double r)
    {
         MessageBox.Show("Amount:" + r.ToString());

    }
 2
Author: Rosemol Francis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-16 07:36:32

1) Najpierw musisz zrozumieć, dlaczego / kiedy potrzebujesz delegata, jaki jest problem, który rozwiązuje.

Z mojego doświadczenia wynika, że używam ich głównie po to, aby umożliwić użytkownikowi dostosowanie zachowania obiektu .

Immagine agrid komponent, który pozwala programistom dostosować sposób renderowania każdej kolumny. Na przykład chcesz zapisać wartość koloru czerwonego, gdy jest to liczba poniżej zera.

Programista tworzący siatkę nie wie jak użytkownik chcesz dostosować wyjście tak, aby wymagało mechanizmu, który pozwala użytkownikowi komponentu wprowadzić trochę logiki do komponentu .

2) Następnie musisz zrozumieć, jak działa delegat

To, co jest mylące, todziwny kod, który musisz napisać, aby to zrobić, ana wiele sposobów musisz zrobić to samo.

Jest to klasa siatki:

// the grid let the programmer that will use it to customize the output
public class Grid{

    // 1) First I declare only the interface of the delegate
    public delegate String ValueFormatterDelegate(String v);

    // 2) I declare a handler of the implementation of the delegate
    public ValueFormatterDelegate ValueFormatterHandler; 

    // 3) I call the handler inside the Print method
    public void Print(String x){
        Console.WriteLine( ValueFormatterHandler.Invoke(x) );
    }

}

/ / 1) Najpierw deklaruję tylko interfejs delegata publiczne delegate String ValueFormatterDelegate (String v);

Zauważ, że jest to jak normalna metoda, ale:

  • posiada delegat słowo kluczowe
  • nie ma implementacji

W ten sposób mówię: "metoda, która będzie formatować wyjście ma taki interfejs: będzie przyjmować ciąg znaków jako wejście i będzie wyprowadzać ciąg znaków"

To dla mnie definicja metody interfejsu.

// 2) ogłaszam opiekunem o wykonaniu delegata public ValueFormatterDelegate ValueFormatterHandler;

Teraz muszę stworzyć właściwość typu delegata, która będzie obsługiwać implementację tej metody.

/ / 3) wywołuję obsługę wewnątrz metody Print public void Print (String x){ Konsola.WriteLine (ValueFormatterHandler.Invoke (x) ); }

Wewnątrz metody Print mogę użyć obsługi, która połączy prawdziwe wdrożenie.

ValueFormatterHandler jest typu ValueFormatterDelegate a ValueFormatterDelegate to ad delegate i .Wywołanie jest metodą typu delegate

Jest to program, który korzysta z mojej klasy Grid i jest w stanie spersonalizować go w locie. Problem polega na tym, że na wiele sposobów musisz zrobić to samo.

using System;

public class Program{
    public static void Main(){

        var printer = new Printer();

        // METHOD 1 : link to a named method
        // here i link the handler of the delegate to a real method
        // the FormatXXX is a static method defined at the ed of this code
        printer.ValueFormatter = FormatXXX;

        // when i call Print("hello")
        printer.Print("hello"); // XXhelloXX

        // METHOD 2 : anonimous method
        // think at this like a method but without a name
        // FormatYY (String x ){ return "YY"+x+"YY"; };
        //  become
        // delegate (String x ){ return "YY"+x+"YY"; };
        printer.ValueFormatter = delegate (String x ){ return "YY"+x+"YY"; };
        printer.Print("hello"); // YYhelloYY

        // METHOD 3 : anonimous method using lambda
        // as you can note the type of parameter x is inferred from the delegate declaration
        // public delegate String ValueFormatterDelegate(String v);
        printer.ValueFormatter = (x)=>"KK" + x + "KK";

    }

    public static String FormatXXX(String y){
        return "XX"+ y +"XX";
    }

}
 2
Author: Marco Staffoli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-14 09:18:55

Delegaty są typem odniesienia, delegat odnosi się do metody. To się nazywa enkapsulowanie metody. Podczas tworzenia delegata należy określić podpis metody i typ powrotu. Możesz zamknąć dowolną metodę dopasowania z tym delegatem. Tworzysz delegata za pomocą słowa kluczowego delegate, po którym następuje Typ zwracania i sygnatury metod, które mogą być do niego delegowane, jak w poniższym przykładzie:

public delegate void HelloFunctionDelegate(string message);
class Program
{
 static void Main()
{
HelloFunctionDelegate del = new HelloFunctionDelegate(Hello);
del("Hello from Delegate");
}
public static void Hello(string strMessage)
{
 Console.WriteLine(strMessage);
}
}

Output is Hello from Delegate

 1
Author: Gagan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-15 07:33:03

Delagates w c#: definiuje sygnaturę metody, którą może invoke.In innymi słowy możemy powiedzieć, że owija odniesienie do metody, którą może wywołać. Poniżej znajdują się zastosowania delegaci :

  1. dostarcza mechanizm do implementacji funkcji call back w. NET framework.
  2. zapewnia możliwość sekwencyjnego wywoływania wielu metod.
  3. posiada możliwość implementacji metody asynchronicznej dzwonię.

Obsługuje zarówno metody statyczne, jak i instancyjne.

Poniżej znajduje się wyjaśnienie, jak to działa wewnętrznie.

/ / Oto deklaracja delegatów.

public delegate void DisplayNamme(string name);

W czasie wykonywania CLR tworzy klasę dla delegatów, jak pokazano poniżej.

public class DisplayNamme : System.MulticastDelegate{

   // It is a contructor
   public DisplayNamme(Object @object, IntPtr method);

   // It is the method with the same prototype as defined in the source code. 
   public void Invoke(String name);

// This method allowing the callback to be asynchronouslly.

 public virtual IAsyncResult BeginInvoke(String name, 
 AsyncCallback callback, Object @object); 

 public virtual void EndInvoke(IAsyncResult result); 

}

Możemy to zobaczyć przez Ildazm.exe Narzędzie . Użyj tego narzędzia, aby złamać bibliotekę DLL.

Konstruktor ma dwa parametry: IntPrt odwołuje się do nazwy metody, która jest przekazywana do funkcji, a @object odnosi się do odniesienia obiektu, który jest przekazywany do konstruktora w sposób niejawny.

CLR używa metody Invoke delegatów do wywołania metody callback.

Poniżej znajduje się implementacja metody callback przy użyciu delegatów.

// Declare Delegates
    public delegate void DisplayNamme(string name);
    class Program
    {
       public static void getName(string name)
        {
            Console.WriteLine(name);
        }
       public static void ShowName(DisplayNamme dn, string name)
        {
        // callback method calling. We can call it in two ways. 
           dn(name);
          // or explicitly
            dn.Invoke(name);
    }
      static void Main(string[] args)
        {
            DisplayNamme delDN = getName;
            Program.ShowName(delDN, "CallBack");
            Console.ReadLine();
        }
    }
 1
Author: Sheo Dayal Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-01 18:57:06

Delegat jest zmienną typu odniesienia, która wskazuje odniesienie do metody. Wszyscy delegaci pochodzą z systemu.Klasa delegatów. Na przykład w Windows Forms lub WPF Zdarzenie metody działa z koncepcją delegatów. Jest to przykład użycia delagatów w C # Wprowadzenie do delegatów w C#

 0
Author: user4340666,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-12 20:29:07