Dela via


System.Delegate och nyckelordet delegate

Föregående

Den här artikeln beskriver klasserna i .NET som stöder ombud och hur de mappar till nyckelordet delegate .

Definiera ombudstyper

Vi börjar med nyckelordet "ombud", eftersom det främst är det du använder när du arbetar med ombud. Koden som kompilatorn genererar när du använder nyckelordet delegate mappas till metodanrop som anropar medlemmar i Delegate klasserna och MulticastDelegate .

Du definierar en ombudstyp med syntax som liknar att definiera en metodsignatur. Du lägger bara till nyckelordet delegate i definitionen.

Låt oss fortsätta att använda metoden List.Sort() som vårt exempel. Det första steget är att skapa en typ för jämförelsedelegaten:

// From the .NET Core library

// Define the delegate type:
public delegate int Comparison<in T>(T left, T right);

Kompilatorn genererar en klass som härleds från System.Delegate och som matchar signaturen som används (i det här fallet en metod som returnerar ett heltal och har två argument). Typen av ombud är Comparison. Ombudstypen Comparison är en allmän typ. Mer information om generiska data finns här.

Observera att syntaxen kan se ut som om den deklarerar en variabel, men att den faktiskt deklarerar en typ. Du kan definiera ombudstyper i klasser, direkt i namnområden eller till och med i det globala namnområdet.

Kommentar

Att deklarera ombudstyper (eller andra typer) direkt i det globala namnområdet rekommenderas inte.

Kompilatorn genererar också hanterare för att lägga till och ta bort för den här nya typen så att klienter i den här klassen kan lägga till och ta bort metoder från en instans anropslista. Kompilatorn framtvingar att signaturen för metoden som läggs till eller tas bort matchar signaturen som används när metoden deklareras.

Deklarera instanser av ombud

När du har definierat ombudet kan du skapa en instans av den typen. Precis som alla variabler i C# kan du inte deklarera delegerade instanser direkt i ett namnområde eller i det globala namnområdet.

// inside a class definition:

// Declare an instance of that type:
public Comparison<T> comparator;

Variabeltypen är Comparison<T>, den ombudstyp som definierades tidigare. Namnet på variabeln är comparator.

Kodfragmentet ovan deklarerade en medlemsvariabel i en klass. Du kan också deklarera delegera variabler som är lokala variabler eller argument till metoder.

Anropa ombud

Du anropar de metoder som finns i anropslistan för ett ombud genom att anropa ombudet. Sort() I metoden anropar koden jämförelsemetoden för att avgöra vilken ordning objekten ska placera:

int result = comparator(left, right);

På raden ovan anropar koden den metod som är kopplad till ombudet. Du behandlar variabeln som ett metodnamn och anropar den med normal metodanropssyntax.

Den kodraden gör ett osäkert antagande: Det finns ingen garanti för att ett mål har lagts till i ombudet. Om inga mål har kopplats skulle linjen ovan leda till att en NullReferenceException utlöses. De idiom som används för att lösa det här problemet är mer komplicerade än en enkel null-kontroll och beskrivs senare i den här serien.

Tilldela, lägga till och ta bort anropsmål

Det är så en delegattyp definieras och hur delegerade instanser deklareras och anropas.

Utvecklare som vill använda List.Sort() metoden måste definiera en metod vars signatur matchar definitionen av ombudstyp och tilldela den till ombudet som används av sorteringsmetoden. Den här tilldelningen lägger till metoden i listan över anrop för det delegerade objektet.

Anta att du vill sortera en lista med strängar efter deras längd. Jämförelsefunktionen kan vara följande:

private static int CompareLength(string left, string right) =>
    left.Length.CompareTo(right.Length);

Metoden deklareras som en privat metod. Det är okej. Du kanske inte vill att den här metoden ska ingå i det offentliga gränssnittet. Den kan fortfarande användas som jämförelsemetod när den är kopplad till ett ombud. Den anropande koden har den här metoden kopplad till mållistan för ombudsobjektet och kan komma åt det via det ombudet.

Du skapar den relationen genom att skicka den metoden till List.Sort() metoden:

phrases.Sort(CompareLength);

Observera att metodnamnet används, utan parenteser. Med metoden som argument uppmanas kompilatorn att konvertera metodreferensen till en referens som kan användas som ett mål för ombudsanrop och bifoga metoden som ett anropsmål.

Du kunde också ha varit explicit genom att deklarera en variabel av typen Comparison<string> och göra en tilldelning:

Comparison<string> comparer = CompareLength;
phrases.Sort(comparer);

I användningar där metoden som används som ombudsmål är en liten metod är det vanligt att använda lambda-uttryckssyntax för att utföra tilldelningen:

Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer);

Användning av lambda-uttryck för ombudsmål beskrivs mer i ett senare avsnitt.

Exemplet Sort() kopplar vanligtvis en enda målmetod till ombudet. Ombudsobjekt stöder dock anropslistor som har flera målmetoder kopplade till ett ombudsobjekt.

Delegera och MulticastDelegate-klasser

Det språkstöd som beskrivs ovan innehåller de funktioner och den support som du vanligtvis behöver för att arbeta med ombud. Dessa funktioner bygger på två klasser i .NET Core-ramverket: Delegate och MulticastDelegate.

Klassen System.Delegate och dess enda direkta underklass, System.MulticastDelegate, ger ramverksstöd för att skapa ombud, registrera metoder som ombudsmål och anropa alla metoder som är registrerade som ett ombudsmål.

Intressant nog är klasserna System.Delegate och System.MulticastDelegate inte själva ombudstyperna. De utgör grunden för alla specifika ombudstyper. Samma språkdesignprocess gav i uppdrag att du inte kan deklarera en klass som härleds från Delegate eller MulticastDelegate. C#-språkreglerna förbjuder det.

I stället skapar C#-kompilatorn instanser av en klass som härleds från MulticastDelegate när du använder nyckelordet C#-språk för att deklarera ombudstyper.

Den här designen har sina rötter i den första versionen av C# och .NET. Ett mål för designteamet var att säkerställa att språket framtvingade typsäkerhet vid användning av ombud. Det innebar att se till att delegaterna anropades med rätt typ och antal argument. Och att alla returtyper var korrekt angivna vid kompileringstillfället. Ombud var en del av 1.0 .NET-versionen, som var före generiska objekt.

Det bästa sättet att framtvinga den här typen av säkerhet var att kompilatorn skapade de konkreta ombudsklasser som representerade metodsignaturen som användes.

Även om du inte kan skapa härledda klasser direkt använder du de metoder som definierats i dessa klasser. Nu ska vi gå igenom de vanligaste metoderna som du kommer att använda när du arbetar med ombud.

Det första, viktigaste faktumet att komma ihåg är att varje ombud som du arbetar med härleds från MulticastDelegate. Ett multicast-ombud innebär att fler än ett metodmål kan anropas när du anropar via ett ombud. Den ursprungliga designen övervägde att skilja mellan ombud där endast en målmetod kunde kopplas och anropas och ombud där flera målmetoder kunde kopplas och anropas. Denna distinktion visade sig vara mindre användbar i praktiken än vad man ursprungligen trodde. De två olika klasserna har redan skapats och har funnits i ramverket sedan den första offentliga versionen.

De metoder som du använder mest med ombud är Invoke() och BeginInvoke() / EndInvoke(). Invoke() anropar alla metoder som har kopplats till en viss delegatinstans. Som du såg ovan anropar du vanligtvis ombud med hjälp av metodens anropssyntax för delegatvariabeln. Som du ser senare i den här serien finns det mönster som fungerar direkt med dessa metoder.

Nu när du har sett språksyntaxen och de klasser som stöder ombud ska vi undersöka hur starkt inskrivna ombud används, skapas och anropas.

Nästa