Jakie są twoje użycie delegatów w C #?
Jakie są twoje użycie delegatów w C #?
Odpowiedzi:
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:
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");
}
}
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.
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.
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] );
} );
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!
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);
}
}
Nieco innym zastosowaniem jest przyspieszenie refleksji; tj. zamiast za każdym razem używać refleksji, możesz użyć Delegate.CreateDelegate
do 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ą Expression
możesz również zrobić to samo, aby utworzyć kod w locie - na przykład możesz łatwo utworzyć Expression
operator + 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ć Expression
do delegata wpisanego na maszynie - zadanie wykonane.
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.
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.
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.
Do obsługi zdarzeń
Aby przekazać metodę w parametrach metody
Wydarzenia, inne operacje anynch
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ć.
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.
Korzystanie z delegatów
Parametr porównania w In Array.Sort (T [] tablica, Porównanie porównania), List.Sort (Porównanie porównania) itp.
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();
}
}