Delegaty o jednoznacznie określonym typie
W poprzednim artykule pokazano, że tworzysz określone typy delegatów przy użyciu słowa kluczowego delegate
.
Abstrakcyjna klasa Delegat udostępnia infrastrukturę luźnego sprzężenia i wywołania. Konkretne typy delegatów stają się znacznie bardziej przydatne, stosując i wymuszając bezpieczeństwo typu dla metod dodanych do listy wywołań dla obiektu delegata. Jeśli używasz słowa kluczowego delegate
i zdefiniuj konkretny typ delegata, kompilator generuje te metody.
W praktyce doprowadziłoby to do utworzenia nowych typów delegatów za każdym razem, gdy potrzebujesz innego podpisu metody. Ta praca może być żmudna po upływie czasu. Każda nowa funkcja wymaga nowych typów delegatów.
Na szczęście nie jest to konieczne. Platforma .NET Core zawiera kilka typów, które można ponownie użyć zawsze, gdy potrzebujesz typów delegatów. Są to definicje ogólne , dzięki czemu można zadeklarować dostosowania, gdy potrzebujesz nowych deklaracji metod.
Pierwszy z tych typów to Action typ i kilka odmian:
public delegate void Action();
public delegate void Action<in T>(T arg);
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
// Other variations removed for brevity.
Modyfikator in
argumentu typu ogólnego został omówiony w artykule dotyczącym wariancji.
Istnieją odmiany delegata Action
, które zawierają maksymalnie 16 argumentów, takich jak Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>.
Ważne jest, aby te definicje używały różnych argumentów ogólnych dla każdego argumentu delegata: zapewnia to maksymalną elastyczność. Argumenty metody nie muszą być, ale mogą być tego samego typu.
Użyj jednego z Action
typów dla dowolnego typu delegata, który ma typ zwracany void.
Struktura zawiera również kilka ogólnych typów delegatów, których można użyć dla typów delegatów, które zwracają wartości:
public delegate TResult Func<out TResult>();
public delegate TResult Func<in T1, out TResult>(T1 arg);
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
// Other variations removed for brevity
Modyfikator out
argumentu typu ogólnego wyniku został omówiony w artykule dotyczącym kowariancji.
Istnieją odmiany delegata Func
z maksymalnie 16 argumentami wejściowymi, takimi jak Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>.
Typ wyniku jest zawsze ostatnim parametrem typu we wszystkich deklaracjach Func
zgodnie z konwencją.
Użyj jednego z Func
typów dla dowolnego typu delegata, który zwraca wartość.
Istnieje również wyspecjalizowany Predicate<T> typ delegata, który zwraca test dla pojedynczej wartości:
public delegate bool Predicate<in T>(T obj);
Można zauważyć, że w przypadku dowolnego Predicate
typu istnieje typ strukturalnie równoważny Func
, na przykład:
Func<string, bool> TestForString;
Predicate<string> AnotherTestForString;
Te dwa typy mogą być równoważne. Nie jest to prawdą. Tych dwóch zmiennych nie można używać zamiennie. Nie można przypisać zmiennej jednego typu innego typu. System typów języka C# używa nazw zdefiniowanych typów, a nie struktury.
Wszystkie te definicje typów delegatów w bibliotece .NET Core powinny oznaczać, że nie trzeba definiować nowego typu delegata dla żadnej nowej funkcji, która wymaga delegatów. Te definicje ogólne powinny zawierać wszystkie typy delegatów, których potrzebujesz w większości sytuacji. Wystarczy utworzyć wystąpienie jednego z tych typów z wymaganymi parametrami typu. W przypadku algorytmów, które mogą być ogólne, te delegaty mogą być używane jako typy ogólne.
Powinno to zaoszczędzić czas i zminimalizować liczbę nowych typów, które należy utworzyć, aby pracować z delegatami.
W następnym artykule zobaczysz kilka typowych wzorców pracy z delegatami w praktyce.