Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
System.Delegate a „
Tento článek popisuje třídy v .NET, které podporují delegáty a jak odpovídají klíčovému slovu delegate
.
Definování typů delegátů
Začněme klíčovým slovem "delegát", protože to je primárně to, co použijete při práci s delegáty. Kód, který kompilátor vygeneruje při použití klíčového delegate
slova, bude mapovat na volání metody, která vyvolá členy Delegate a MulticastDelegate třídy.
Delegátový typ definujete pomocí syntaxe, která se podobá definování signatury metody. Stačí přidat delegate
klíčové slovo do definice.
Pojďme jako příklad dál používat metodu List.Sort(). Prvním krokem je vytvoření typu pro delegáta porovnání:
// From the .NET Core library
// Define the delegate type:
public delegate int Comparison<in T>(T left, T right);
Kompilátor generuje třídu odvozenou od System.Delegate
, která odpovídá použitým podpisem (v tomto případě metodě, která vrací celé číslo a má dva argumenty). Typ tohoto delegáta je Comparison
. Typ delegáta Comparison
je obecný typ. Další informace naleznete v tématu Obecné třídy a metody.
Všimněte si, že syntaxe se může zdát, jako by deklaruje proměnnou, ale ve skutečnosti deklaruje typ. Můžete definovat typy delegátů uvnitř tříd, přímo uvnitř oborů názvů nebo dokonce v globálním oboru názvů.
Poznámka:
Deklarování typů delegátů (nebo jiných typů) přímo v globálním oboru názvů se nedoporučuje.
Kompilátor také generuje obslužné rutiny pro přidání a odebrání pro tento nový typ, aby klienti této třídy mohli přidávat a odebírat metody ze seznamu vyvolání instance. Kompilátor vynutí, aby podpis přidané nebo odebrané metody odpovídal podpisu použitému při deklarování metody.
Deklarace instancí delegátů
Po definování delegáta můžete vytvořit instanci tohoto typu. Stejně jako všechny proměnné v jazyce C# nemůžete deklarovat instance delegátů přímo v oboru názvů ani v globálním oboru názvů.
// inside a class definition:
// Declare an instance of that type:
public Comparison<T> comparator;
Typ proměnné je Comparison<T>
, typ delegáta definovaný dříve. Název proměnné je comparator
.
Výše uvedený fragment kódu deklaroval členovou proměnnou uvnitř třídy. Můžete také deklarovat delegování proměnných, které jsou místními proměnnými, nebo argumenty metodám.
Vyvolání delegátů
Vyvoláte metody, které jsou v seznamu volání delegáta, tím, že zavoláte tohoto delegáta.
Sort()
Uvnitř metody kód zavolá metodu porovnání k určení pořadí, ve kterém se mají umístit objekty:
int result = comparator(left, right);
V kódu výše spustí metodu připojenou k delegátu. Proměnnou považujete za název metody a vyvoláte ji pomocí syntaxe volání normální metody.
Tento řádek kódu představuje nebezpečný předpoklad: Neexistuje žádná záruka, že byl do delegáta přidán cíl. Pokud nebyly připojeny žádné cíle, výše uvedený řádek by způsobil NullReferenceException
vyvolání. Idiomy používané k vyřešení tohoto problému jsou složitější než jednoduchá kontrola null a jsou popsány dále v této sérii.
Přiřazení, přidání a odebrání cílů vyvolání
Takto je definován typ delegáta a jak jsou deklarovány a vyvolány instance delegáta.
Vývojáři, kteří chtějí použít metodu List.Sort()
, musí definovat metodu, jejíž podpis odpovídá definici typu delegáta, a přiřadit ji delegátovi používanému metodou řazení. Toto přiřazení přidá metodu do seznamu vyvolání tohoto objektu delegáta.
Předpokládejme, že jste chtěli seřadit seznam řetězců podle jejich délky. Funkce porovnání může být následující:
private static int CompareLength(string left, string right) =>
left.Length.CompareTo(right.Length);
Metoda je deklarována jako soukromá metoda. To je v pořádku. Tuto metodu možná nebudete chtít, aby byla součástí vašeho veřejného rozhraní. Při připojení k delegátu se dá stále použít jako porovnávací metoda. Volající kód bude mít tuto metodu připojenou k cílovému seznamu objektu delegáta a může k němu přistupovat prostřednictvím tohoto delegáta.
Tuto relaci vytvoříte předáním této metody metodě List.Sort()
:
phrases.Sort(CompareLength);
Všimněte si, že se používá název metody bez závorek. Použití metody jako argumentu dává kompilátoru pokyn převést odkaz na metodu na odkaz, který lze použít jako cíl vyvolání delegáta, a připojit tuto metodu jako cílový bod pro vyvolání.
Mohli jste také být explicitní tím, že deklarujete proměnnou typu Comparison<string>
a provedete přiřazení.
Comparison<string> comparer = CompareLength;
phrases.Sort(comparer);
V případech, kdy metoda použitá jako cíl delegáta je malá metoda, je běžné použít syntaxi výrazu lambda k provedení přiřazení:
Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer);
Použití výrazů lambda pro cíle delegáta je popsáno více v další části.
Příklad Sort() obvykle připojuje jednu cílovou metodu k delegátu. Objekty delegáta však podporují seznamy volání, které mají k objektu delegáta připojeno více cílových metod.
Třídy Delegate a MulticastDelegate
Výše popsaná podpora jazyka poskytuje funkce a podporu, které obvykle potřebujete pracovat s delegáty. Tyto funkce jsou založené na dvou třídách v rozhraní .NET Core: Delegate a MulticastDelegate.
Třída System.Delegate
a její jediná přímá podtřída, System.MulticastDelegate
poskytuje podporu architektury pro vytváření delegátů, registraci metod jako cíle delegátů a vyvolání všech metod, které jsou registrovány jako cíl delegáta.
Zajímavé je, že třídy System.Delegate
a System.MulticastDelegate
nejsou samy o sobě typy delegátů. Poskytují základ pro všechny konkrétní typy delegátů. Stejný proces návrhu jazyka vyžaduje, abyste nemohli deklarovat třídu, která je odvozena z Delegate
nebo MulticastDelegate
. Pravidla jazyka C# ji zakazují.
Místo toho kompilátor jazyka C# vytvoří instance třídy odvozené od MulticastDelegate
při použití klíčového slova jazyka C# k deklaraci typů delegátů.
Tento návrh má své kořeny v prvním vydání jazyka C# a .NET. Jedním z cílů návrhového týmu bylo zajistit, aby jazyk vynucoval bezpečnost typů při používání delegátů. To znamená zajistit, aby delegáti byli vyvoláni se správným typem a počtem argumentů. A že jakýkoli návratový typ byl správně označen v době kompilace. Delegáti byli součástí verze 1.0 .NET, která byla před obecnými typy.
Nejlepším způsobem, jak vynutit tuto bezpečnost typů, byl pro kompilátor vytvořit konkrétní třídy delegátů, které představovaly použitý podpis metody.
I když nemůžete přímo vytvořit odvozené třídy, použijete metody definované v těchto třídách. Pojďme si projít nejběžnější metody, které budete používat při práci s delegáty.
Prvním, nejdůležitějším faktem, který si pamatujete, je, že každý delegát, se kterým pracujete, je odvozen od MulticastDelegate
. Delegát rozesílání znamená, že při použití delegáta lze spustit více než jednu metodu. Původní návrh považoval za rozdíl mezi delegáty, kde lze připojit a vyvolat pouze jednu cílovou metodu, a delegáty, u kterých by bylo možné připojit a vyvolat více cílových metod. Tento rozdíl se ukázal jako méně užitečný v praxi, než původně myslel. Dvě různé třídy byly již vytvořeny a jsou součástí frameworku od jeho první veřejné verze.
Metody, které budete používat nejvíce s delegáty jsou Invoke()
a BeginInvoke()
/ EndInvoke()
.
Invoke()
zavolá všechny metody, které byly připojeny ke konkrétní delegátní instanci. Jak jste viděli výše, delegáty obvykle vyvoláváte pomocí syntaxe volání metody na proměnné delegáta. Jak uvidíte později v této sérii, existují vzory, které pracují přímo s těmito metodami.
Teď, když jste viděli syntaxi jazyka a třídy, které podporují delegáty, pojďme se podívat, jak se používají, vytvářejí a vyvolávají delegáti silného typu.