Sdílet prostřednictvím


System.Delegate a delegate klíčové slovo

Předchozí

Tento článek popisuje třídy v .NET, které podporují delegáty a jak se mapují na delegate klíčové slovo.

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.

Delegování typu definujete pomocí syntaxe, která se podobá definování podpisu 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 toho, která odpovídá použitému podpisu (v tomto případě metoda, která vrací celé číslo a má dva argumenty). Typ tohoto delegáta je Comparison. Typ delegáta Comparison je obecný typ. Podrobnosti o obecných souborech najdete tady.

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 delegování instancí 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 vyvolání delegáta voláním 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);

Na řádku výše vyvolá kód 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 metoda porovnání. 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 říká kompilátoru, aby převeďte odkaz na metodu na odkaz, který lze použít jako cíl vyvolání delegáta, a připojit tuto metodu jako cíl vyvolání.

Můžete také explicitně deklarovat proměnnou typu Comparison<string> a přiřazením:

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řipojí jednu cílovou metodu 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.MulticastDelegateposkytuje 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 typy delegátů System.Delegate nejsou samy o System.MulticastDelegate sobě. 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 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 se při používání delegátů vynucovala bezpečnost typů vynucování jazyka. 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 vícesměrového vysílání znamená, že při vyvolání prostřednictvím delegáta lze vyvolat více než jeden cíl metody. 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 byly v rámci od jeho počáteční veřejné verze.

Metody, které budete používat nejvíce s delegáty jsou Invoke() a BeginInvoke() / EndInvoke(). Invoke() vyvolá všechny metody, které byly připojeny ke konkrétní instanci delegáta. Jak jste viděli výše, obvykle vyvoláte delegáty pomocí syntaxe volání metody v 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.

Další