Kiedy będziesz używać delegatów w C #? [Zamknięte]


101

Jakie są twoje użycie delegatów w C #?


2
Czy masz na myśli delegatów w systemie typów .NET lub składni delegata C #? Czy masz na myśli „kiedy używasz składni delegata zamiast składni wyrażenia lambda”, czy też „kiedy używasz delegatów zamiast klas / interfejsów / metod wirtualnych / itp.”?
Niki

Odpowiedzi:


100

Teraz, gdy mamy wyrażenia lambda i metody anonimowe w C #, znacznie częściej używam delegatów. W C # 1, gdzie zawsze trzeba było mieć osobną metodę implementacji logiki, używanie delegata często nie miało sensu. Obecnie używam delegatów do:

  • Programy obsługi zdarzeń (dla GUI i nie tylko)
  • Rozpoczynanie wątków
  • Wywołania zwrotne (np. Dla asynchronicznych interfejsów API)
  • LINQ i podobne (List.Find itp.)
  • Wszędzie indziej, gdzie chcę efektywnie zastosować kod „szablonu” z pewną wyspecjalizowaną logiką (gdzie delegat zapewnia specjalizację)

Warto wspomnieć o „push” w Push LINQ?
Marc Gravell

3
Nie jestem pewien, jak mógłbym to krótko wyjaśnić, nie robiąc rzeczy bardziej zagmatwanymi :) (Prawdopodobnie jest to objęte obsługą zdarzeń, LINQ i bitem templaty i tak!
Jon Skeet

1
Twoje pierwsze zdanie nie ma większego sensu.
senfo

3
Wiem, co próbujesz powiedzieć, ale łatwiej byłoby mi przeczytać następujący tekst: „Teraz, gdy mamy wyrażenia lambda i metody anonimowe w języku C #, znacznie częściej używam delegatów”. Wiem, że czepiam się dzioba, ale naprawdę musiałem przeczytać to zdanie kilka razy, zanim nabrało to dla mnie sensu.
senfo

4
+1 za odwagę rzucenia wyzwania czcigodnemu panu Skeetowi ;-)
indra

29

Delegaci są bardzo przydatni do wielu celów.

Jednym z takich celów jest użycie ich do filtrowania sekwencji danych. W tym przypadku należy użyć delegata predykatu, który akceptuje jeden argument i zwraca wartość true lub false w zależności od implementacji samego delegata.

Oto głupi przykład - jestem pewien, że możesz ekstrapolować z tego coś bardziej użytecznego:

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");
    }
}

11
static Boolean inMyFamily(String name)Metodą jest pełnomocnika. Where przyjmuje delegata jako parametr. Ponieważ delegaci są po prostu wskaźnikami funkcji, gdy przekazujesz nazwę metody do elementu, .Where(delegate)który staje się delegatem. Ponieważ inMyFamily zwraca typ boolowski, w rzeczywistości jest traktowany jako predykat. Predykaty są po prostu delegatami, które zwracają wartości logiczne.
Landon Poch

4
„Predykaty są po prostu delegatami, które zwracają wartości logiczne”. +1
daehaai

@LandonPoch ten komentarz byłby lepiej umieszczony w odpowiedzi. jako początkujący nie mogłem zrozumieć, gdzie to jest. dzięki.
Eakan Gopalakrishnan

@Eakan, tak naprawdę nie odpowiadałem na główne pytanie (kiedy użyjesz delegatów), więc zostawiłem to jako komentarz.
Landon Poch

14

Znalazłem inną interesującą odpowiedź:

Współpracownik właśnie zadał mi to pytanie - jaki jest sens delegatów w .NET? Moja odpowiedź była bardzo krótka i taka, której nie znalazł w Internecie: opóźnić wykonanie metody.

Źródło: LosTechies

Tak jak robi LINQ.


+1 .. nie myślałem o tym w ten sposób.
Słuszna

12

Za pomocą delegatów można deklarować zmienne i parametry typu funkcji.

Przykład

Rozważmy wzorzec „pożyczania zasobów”. Chcesz kontrolować tworzenie i czyszczenie zasobu, jednocześnie pozwalając kodowi klienta na „wypożyczenie” zasobu w międzyczasie.

To deklaruje typ delegata.

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

Do utworzenia wystąpienia delegata tego typu można użyć dowolnej metody pasującej do tego podpisu. W C # 2.0 można to zrobić niejawnie, po prostu używając nazwy metody, jak również używając metod anonimowych.

Ta metoda używa typu jako parametru. Zwróć uwagę na wywołanie delegata.

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;
        }
    }
}

Funkcję można wywołać metodą anonimową w następujący sposób. Zauważ, że metoda anonimowa może używać zmiennych zadeklarowanych poza nią. Jest to niezwykle przydatne (chociaż przykład jest trochę wymyślony).

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] );
    } );

11

W jednej metodzie zamiast interfejsu można często używać delegatów, czego typowym przykładem jest wzorzec obserwatora. W innych językach, jeśli chcesz otrzymać powiadomienie, że coś się stało, możesz zdefiniować na przykład:

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

W C # jest to częściej wyrażane za pomocą zdarzeń, w których program obsługi jest delegatem, na przykład:

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

Kolejne świetne miejsce do użycia delegatów, jeśli musisz przekazać predykat do funkcji, na przykład podczas wybierania zestawu elementów z listy:

myList.Where(i => i > 10);

Powyższe jest przykładem składni lambda, która również mogłaby zostać zapisana następująco:

myList.Where(delegate(int i){ return i > 10; });

Innym miejscem, w którym może być przydatne użycie delegatów, jest rejestracja funkcji fabrycznych, na przykład:

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

Mam nadzieję, że to pomoże!


Ładne przykłady ze składnią .. Dzięki .. :)
Raghu

10

Przychodzę do tego bardzo późno, ale miałem dziś problem ze zrozumieniem celu delegatów i napisałem dwa proste programy, które dają te same wyniki, co moim zdaniem dobrze wyjaśnia ich cel.

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);
    }
}

5

Nieco innym zastosowaniem jest przyspieszenie refleksji; tj. zamiast za każdym razem używać refleksji, możesz użyć Delegate.CreateDelegatedo utworzenia (wpisanego na maszynie) delegata do metody (a MethodInfo) i zamiast tego wywołać tego delegata. Jest to wtedy znacznie szybsze w przypadku każdego połączenia, ponieważ kontrole zostały już wykonane.

Za pomocą Expressionmożesz również zrobić to samo, aby utworzyć kod w locie - na przykład możesz łatwo utworzyć Expressionoperator + dla typu wybranego w czasie wykonywania (aby zapewnić obsługę operatora dla typów generycznych, których język nie zapewnia) ; i można skompilować Expressiondo delegata wpisanego na maszynie - zadanie wykonane.


5

Delegaci są wykorzystywani za każdym razem, gdy używasz zdarzeń - to mechanizm, dzięki któremu działają.

Ponadto delegaci są bardzo przydatni do takich rzeczy, jak używanie zapytań LINQ. Na przykład wiele zapytań LINQ przyjmuje delegata (często Func<T,TResult>), którego można użyć do filtrowania.



2

Przykładem może być tutaj . Masz metodę przetwarzania obiektu, który spełnia określone wymagania. Jednak chcesz mieć możliwość przetwarzania obiektu na wiele sposobów. Zamiast tworzyć oddzielne metody, możesz po prostu przypisać metodę dopasowującą, która przetwarza obiekt delegatowi i przekazać delegata do metody, która wybiera obiekty. W ten sposób możesz przypisać różne metody do jednej metody selektora. Starałem się, aby było to zrozumiałe.


1

Używam delegatów do komunikacji z wątkami.

Na przykład mogę mieć aplikację do wygrywania formularzy, która pobiera plik. Aplikacja uruchamia wątek roboczy, aby wykonać pobieranie (co zapobiega blokowaniu się GUI). Wątek roboczy używa delegatów do wysyłania komunikatów o stanie (np. Postęp pobierania) z powrotem do programu głównego, dzięki czemu GUI może aktualizować pasek stanu.



0

Pierwsza linia użycia polega na zastąpieniu wzorca Observer / Observable (zdarzenia). Druga, ładna elegancka wersja wzorca Strategy. Można zebrać różne inne zastosowania, chociaż myślę, że są bardziej ezoteryczne niż te dwa pierwsze.


0

Wydarzenia, inne operacje anynch


0

Zawsze, gdy chcesz hermetyzować zachowanie, ale wywołuj je w jednolity sposób. Programy obsługi zdarzeń, funkcje zwrotne itp. Możesz osiągnąć podobne rzeczy za pomocą interfejsów i rzutowań, ale czasami zachowanie niekoniecznie jest powiązane z typem lub obiektem . Czasami po prostu masz zachowanie, które musisz hermetyzować.


0

Leniwa inicjalizacja parametrów! Oprócz wszystkich poprzednich odpowiedzi (wzorzec strategii, wzorzec obserwatora itp.), Delegaci pozwalają na leniwą inicjalizację parametrów. Na przykład załóżmy, że masz funkcję Download (), która zajmuje sporo czasu i zwraca pewien DownloadedObject. Ten obiekt jest używany przez magazyn w zależności od określonych warunków. Zwykle należałoby:

storage.Store(conditions, Download(item))

Jednak z delegatami (a dokładniej lambdami) możesz wykonać następujące czynności, zmieniając podpis sklepu tak, aby otrzymał Condition i Func <Item, DownloadedObject> i używać go w następujący sposób:

storage.Store(conditions, (item) => Download(item))

Dlatego magazyn oceni delegata tylko w razie potrzeby, wykonując pobieranie w zależności od warunków.


Drobna uwaga, ale „dokładniej, lambdy” - możesz zrobić to samo z anonimową metodą w C # 2.0, chociaż byłoby to bardziej szczegółowe: delegate (ItemType item) {[return] Download (item);}
Marc Gravell

Jasne, tak samo jak LINQ: lambdy to nic innego jak cukier składniowy dla delegatów. Po prostu sprawili, że delegaci są bardziej dostępni.
Santiago Palladino

Lambdy to coś więcej niż tylko delegaci, ponieważ można je konwertować na drzewa wyrażeń, a także na delegatów.
Jon Skeet

Cóż, lambdy można również skompilować do wyrażeń, które są zupełnie inne niż delegaty. Ale w twoim przykładzie użyto Func <,>, którego można użyć z metody anon. Wyrażenia byłyby niezwykle bolesne do pisania w C # 2.0.
Marc Gravell


0

Parametr porównania w In Array.Sort (T [] tablica, Porównanie porównania), List.Sort (Porównanie porównania) itp.


0

O ile wiem, delegatów można przekonwertować na wskaźniki funkcji. To znacznie ułatwia życie podczas współpracy z rodzimym kodem, który przyjmuje wskaźniki funkcji, ponieważ mogą one być efektywnie zorientowane obiektowo, nawet jeśli oryginalny programista nie przewidywał tego.


0

Delegaty służą do wywoływania metody przez jej odwołanie. Na przykład:

  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();
        }
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.