Używanie delegatów (Przewodnik programowania w języku C#)
Delegat to typ, który bezpiecznie hermetyzuje metodę, podobnie jak wskaźnik funkcji w języku C i C++. W przeciwieństwie do wskaźników funkcji języka C, delegaty są obiektami zorientowanymi, bezpiecznymi i bezpiecznymi typami. Typ delegata jest definiowany przez nazwę delegata. W poniższym przykładzie zadeklarowano delegata o nazwie Callback
, który może hermetyzować metodę, która przyjmuje ciąg jako argument i zwraca wartość void:
public delegate void Callback(string message);
Obiekt delegata jest zwykle konstruowany przez podanie nazwy metody, którą delegat zawija lub za pomocą wyrażenia lambda. Po utworzeniu wystąpienia delegata w ten sposób można go wywołać. Wywołanie delegata wywołuje metodę dołączoną do wystąpienia delegata. Parametry przekazywane do delegata przez obiekt wywołujący są przekazywane do metody, a wartość zwracana, jeśli istnieje, z metody jest zwracana do obiektu wywołującego przez delegata. Na przykład:
// Create a method for a delegate.
public static void DelegateMethod(string message)
{
Console.WriteLine(message);
}
// Instantiate the delegate.
Callback handler = DelegateMethod;
// Call the delegate.
handler("Hello World");
Typy delegatów pochodzą z Delegate klasy na platformie .NET. Typy delegatów są zapieczętowane — nie mogą pochodzić z — i nie można tworzyć klas niestandardowych z klasy Delegate. Ponieważ wystąpienie delegata jest obiektem, można go przekazać jako argument lub przypisać do właściwości. Dzięki temu metoda może zaakceptować delegata jako parametr i wywołać delegata w późniejszym czasie. Jest to nazywane wywołaniem zwrotnym asynchronicznym i jest to powszechna metoda powiadamiania obiektu wywołującego po zakończeniu długiego procesu. Gdy delegat jest używany w ten sposób, kod używający delegata nie potrzebuje żadnej wiedzy na temat implementacji używanej metody. Funkcje są podobne do interfejsów hermetyzacji.
Innym typowym zastosowaniem wywołań zwrotnych jest zdefiniowanie niestandardowej metody porównania i przekazanie tego delegata do metody sortowania. Dzięki niemu kod obiektu wywołującego staje się częścią algorytmu sortowania. Poniższa przykładowa metoda używa typu jako parametru Del
:
public static void MethodWithCallback(int param1, int param2, Callback callback)
{
callback("The number is: " + (param1 + param2).ToString());
}
Następnie możesz przekazać pełnomocnika utworzonego powyżej do tej metody:
MethodWithCallback(1, 2, handler);
i otrzymaj następujące dane wyjściowe do konsoli:
The number is: 3
Używanie delegata jako abstrakcji MethodWithCallback
nie wymaga bezpośredniego wywoływania konsoli — nie musi być zaprojektowane z myślą o konsoli. Co MethodWithCallback
to jest po prostu przygotowanie ciągu i przekazanie ciągu do innej metody. Jest to szczególnie zaawansowane, ponieważ metoda delegowana może używać dowolnej liczby parametrów.
Gdy delegat jest skonstruowany w celu opakowania metody wystąpienia, delegat odwołuje się zarówno do wystąpienia, jak i metody. Delegat nie ma wiedzy o typie wystąpienia oprócz metody, która opakowuje, więc delegat może odwoływać się do dowolnego typu obiektu, o ile istnieje metoda na tym obiekcie, który pasuje do podpisu delegata. Gdy delegat jest skonstruowany w celu opakowania metody statycznej, odwołuje się tylko do metody . Rozważ następujące deklaracje:
public class MethodClass
{
public void Method1(string message) { }
public void Method2(string message) { }
}
Oprócz pokazanych wcześniej statycznych metod DelegateMethod
mamy teraz trzy metody, które mogą być opakowane przez Del
wystąpienie.
Delegat może wywołać więcej niż jedną metodę podczas wywoływania. Jest to nazywane multiemisji. Aby dodać dodatkową metodę do listy metod delegata — listy wywołań — po prostu wymaga dodania dwóch delegatów przy użyciu operatorów przypisania dodawania lub dodawania ('+' lub '+='). Na przykład:
var obj = new MethodClass();
Callback d1 = obj.Method1;
Callback d2 = obj.Method2;
Callback d3 = DelegateMethod;
//Both types of assignment are valid.
Callback allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;
W tym momencie allMethodsDelegate
na liście wywołań znajdują się trzy metody —Method1
, Method2
i DelegateMethod
. Oryginalne trzy delegaty, d1
, d2
i d3
, pozostają niezmienione. Po allMethodsDelegate
wywołaniu wszystkie trzy metody są wywoływane w kolejności. Jeśli delegat używa parametrów odwołania, odwołanie jest przekazywane sekwencyjnie do każdej z trzech metod z kolei, a wszelkie zmiany według jednej metody są widoczne dla następnej metody. Gdy którakolwiek z metod zgłasza wyjątek, który nie jest przechwytywany w metodzie, ten wyjątek jest przekazywany do obiektu wywołującego delegata i nie są wywoływane żadne kolejne metody na liście wywołań. Jeśli delegat ma wartość zwracaną i/lub wychodzące parametry, zwraca wartość zwracaną i parametry ostatniej wywoływanej metody. Aby usunąć metodę z listy wywołań, użyj operatorów przypisania odejmowania lub odejmowania (-
lub -=
). Na przykład:
//remove Method1
allMethodsDelegate -= d1;
// copy AllMethodsDelegate while removing d2
Callback oneMethodDelegate = allMethodsDelegate - d2;
Ponieważ typy delegatów pochodzą z System.Delegate
klasy , metody i właściwości zdefiniowane przez klasę mogą być wywoływane na delegacie. Aby na przykład znaleźć liczbę metod na liście wywołań delegata, możesz napisać:
int invocationCount = d1.GetInvocationList().GetLength(0);
Delegaci z więcej niż jedną metodą na liście wywołań pochodzą z MulticastDelegateklasy , która jest podklasą System.Delegate
. Powyższy kod działa w obu przypadkach, ponieważ obie klasy obsługują element GetInvocationList
.
Delegaty multiemisji są szeroko używane w obsłudze zdarzeń. Obiekty źródła zdarzeń wysyłają powiadomienia o zdarzeniach do obiektów adresatów zarejestrowanych w celu odbierania tego zdarzenia. Aby zarejestrować się w przypadku zdarzenia, adresat tworzy metodę zaprojektowaną do obsługi zdarzenia, a następnie tworzy delegata dla tej metody i przekazuje delegata do źródła zdarzeń. Źródło wywołuje delegata, gdy wystąpi zdarzenie. Delegat wywołuje następnie metodę obsługi zdarzeń na odbiorcy, dostarczając dane zdarzenia. Typ delegata dla danego zdarzenia jest definiowany przez źródło zdarzeń. Aby uzyskać więcej informacji, zobacz Zdarzenia.
Porównanie delegatów dwóch różnych typów przypisanych w czasie kompilacji spowoduje błąd kompilacji. Jeśli wystąpienia delegatów są statycznie typu System.Delegate
, porównanie jest dozwolone, ale zwróci wartość false w czasie wykonywania. Na przykład:
delegate void Callback1();
delegate void Callback2();
static void method(Callback1 d, Callback2 e, System.Delegate f)
{
// Compile-time error.
//Console.WriteLine(d == e);
// OK at compile-time. False if the run-time type of f
// is not the same as that of d.
Console.WriteLine(d == f);
}