Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
System.Delegate och nyckelordet
Den här artikeln beskriver klasserna i .NET som stöder ombud och hur de mappar till nyckelordet delegate .
Vad är ombud?
Tänk på en delegering som ett sätt att lagra en referens till en metod, ungefär som hur du kan lagra en referens till ett objekt. Precis som du kan skicka objekt till metoder kan du skicka metodreferenser med hjälp av ombud. Detta är användbart när du vill skriva flexibel kod där olika metoder kan "anslutas" för att ge olika beteenden.
Anta till exempel att du har en kalkylator som kan utföra åtgärder på två tal. I stället för att hårdkoda addition, subtraktion, multiplikation och division i separata metoder kan du använda delegater för att representera vilken som helst operation som tar två tal och returnerar ett resultat.
Definiera ombudstyper
Nu ska vi se hur du skapar delegattyper med hjälp av nyckelordet delegate. När du definierar en ombudstyp skapar du i princip en mall som beskriver vilken typ av metoder som kan lagras i ombudet.
Du definierar en ombudstyp med syntax som liknar en metodsignatur, men med nyckelordet delegate i början:
// Define a simple delegate that can point to methods taking two integers and returning an integer
public delegate int Calculator(int x, int y);
Det här Calculator delegeringen kan innehålla referenser till alla metoder som tar två int parametrar och returnerar en int.
Låt oss titta på ett mer praktiskt exempel. När du vill sortera en lista måste du tala om för sorteringsalgoritmen hur objekt ska jämföras. Nu ska vi se hur ombud hjälper till med List.Sort() metoden. Det första steget är att skapa en ombudstyp för jämförelseåtgärden:
// From the .NET Core library
public delegate int Comparison<in T>(T left, T right);
Det här Comparison<T> delegatet kan innehålla referenser till alla metoder som:
- Tar två parametrar av typen T
- Returnerar en int(vanligtvis -1, 0 eller 1 för att ange "mindre än", "lika med" eller "större än")
När du definierar en delegerad typ som denna genererar kompilatorn automatiskt en klass som härstammar från System.Delegate och som matchar din signatur. Den här klassen hanterar alla komplexiteten i att lagra och anropa metodreferenserna åt dig.
Ombudstypen Comparison är en allmän typ, vilket innebär att den kan fungera med valfri typ T. Mer information om generiska objekt finns i Allmänna klasser och metoder.
Observera att även om syntaxen ser ut ungefär som att deklarera en variabel deklarerar du faktiskt en ny typ. Du kan definiera ombudstyper i klasser, direkt i namnområden eller till och med i det globala namnområdet.
Anmärkning
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 ombudstypen deklareras.
Deklarera instanser av ombud
När du har definierat ombudstypen kan du skapa instanser (variabler) av den typen. Tänk på det här som att skapa ett "fack" där du kan lagra en referens till en metod.
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:
public Comparison<T> comparator;
Typen av den här variabeln är Comparison<T> (den ombudstyp som du definierade tidigare) och namnet på variabeln är comparator. I det här läget comparator pekar inte på någon metod ännu – det är som ett tomt fack som väntar på att fyllas.
Du kan också deklarera delegera variabler som lokala variabler eller metodparametrar, precis som andra variabeltyper.
Anropa ombud
När du har en delegerad instans som pekar på en metod kan du kalla på metoden via delegaten. Du anropar de metoder som finns i anropslistan för ett ombud genom att anropa ombudet som om det vore en metod.
              Sort() Så här använder metoden jämförelsedelegaten för att fastställa objektordningen:
int result = comparator(left, right);
På den här raden anropar koden den metod som är kopplad till ombudet. Du behandlar delegatvariabeln som om den vore ett metodnamn och anropar den med normal metodanropssyntax.
Detta kodraden gör dock ett osäkert antagande: det förutsätter att en målmetod har lagts till i delegaten. Om inga metoder har kopplats skulle linjen ovan orsaka att en NullReferenceException utlöses. De mönster som används för att lösa det här problemet är mer avancerade än en enkel null-kontroll och beskrivs senare i den här serien.
Tilldela, lägga till och ta bort anropsmål
Nu vet du hur du definierar ombudstyper, deklarerar delegatinstanser och anropar ombud. Men hur kopplar du en metod till en delegering? Det är här som delegattilldelningen kommer in.
För att använda en delegat måste du tilldela en metod till den. Den metod som du tilldelar måste ha samma signatur (samma parametrar och returtyp) som ombudstypen definierar.
Låt oss se ett praktiskt exempel. Anta att du vill sortera en lista med strängar efter deras längd. Du måste skapa en jämförelsemetod som matchar ombudets Comparison<string> signatur:
private static int CompareLength(string left, string right) =>
    left.Length.CompareTo(right.Length);
Den här metoden tar två strängar och returnerar ett heltal som anger vilken sträng som är "större" (längre i det här fallet). Metoden deklareras som privat, vilket är helt okej. Du behöver inte metoden för att vara en del av det offentliga gränssnittet för att använda den med ett ombud.
Nu kan du skicka den här metoden till List.Sort() metoden:
phrases.Sort(CompareLength);
Observera att du använder metodnamnet utan parenteser. Detta instruerar kompilatorn att konvertera metodreferensen till ett ombud som kan anropas senare. Metoden Sort() anropar din CompareLength metod när den behöver jämföra två strängar.
Du kan också vara tydligare genom att deklarera en delegatvariabel och tilldela metoden till den:
Comparison<string> comparer = CompareLength;
phrases.Sort(comparer);
Båda metoderna åstadkommer samma sak. Den första metoden är mer kortfattad, medan den andra gör delegattilldelningen tydligare.
För enkla metoder är det vanligt att använda lambda-uttryck i stället för att definiera en separat metod:
Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer);
Lambda-uttryck erbjuder ett kompakt sätt att definiera enkla metoder inline. Användning av lambda-uttryck för delegerade mål beskrivs mer detaljerat i ett senare avsnitt.
Exemplen hittills visar delegater med en enda metod för mål. Delegeringsobjekt kan dock ha stöd för anropslistor som har flera målmetoder kopplade till ett enda delegeringsobjekt. Den här funktionen är särskilt användbar för händelsehanteringsscenarier.
Delegate- och MulticastDelegate-klasser
I bakgrunden bygger de delegerade funktioner som du har använt på två nyckelklasser i .NET-ramverket: Delegate och MulticastDelegate. Du arbetar vanligtvis inte med dessa klasser direkt, men de utgör grunden som får ombuden att fungera.
Klassen System.Delegate och dess direkta underklass System.MulticastDelegate ger ramverksstöd för att skapa ombud, registrera metoder som ombudsmål och anropa alla metoder som är registrerade med ett ombud.
Här är en intressant designdetalj: System.Delegate och System.MulticastDelegate är inte delegattyper som du kan använda. I stället fungerar de som basklasser för alla specifika ombudstyper som du skapar. C#-språket hindrar dig från att ärva direkt från dessa klasser – du måste använda nyckelordet delegate i stället.
När du använder nyckelordet delegate för att deklarera en ombudstyp skapar C#-kompilatorn automatiskt en klass som härleds från MulticastDelegate med din specifika signatur.
Varför den här designen?
Den här designen har sina rötter i den första versionen av C# och .NET. Designteamet hade flera mål:
- Typsäkerhet: Teamet ville säkerställa att språket framtvingade typsäkerhet vid användning av ombud. Det innebär att se till att ombud anropas med rätt typ och antal argument och att returtyperna verifieras korrekt vid kompileringstillfället. 
- Prestanda: Genom att kompilatorn genererar konkreta delegeringsklasser som representerar specifika metodsignaturer kan körmiljön optimera och delegera anrop. 
- Enkelhet: Ombud inkluderades i 1.0 .NET-versionen, som var innan generiska objekt introducerades. Den design som krävs för att fungera inom tidens begränsningar. 
Lösningen var att kompilatorn skulle skapa de konkreta ombudsklasser som matchar dina metodsignaturer, vilket säkerställer typsäkerhet samtidigt som komplexiteten döljs för dig.
Arbeta med ombudsmetoder
Även om du inte kan skapa härledda klasser direkt använder du ibland metoder som definierats i klasserna Delegate och MulticastDelegate . Här är de viktigaste att känna till:
Varje ombud som du arbetar med härleds från MulticastDelegate. En "multicast"-delegering innebär att fler än en metod kan anropas när man anropar via en delegering. Den ursprungliga designen övervägde att skilja mellan ombud som bara kunde anropa en metod jämfört med ombud som kunde anropa flera metoder. I praktiken visade sig den här skillnaden vara mindre användbar än vad som ursprungligen troddes, så alla ombud i .NET stöder flera målmetoder.
De vanligaste metoderna när du arbetar med ombud är:
- 
              Invoke(): Anropar alla metoder som är kopplade till ombudet
- 
              BeginInvoke()/EndInvoke(): Används för asynkrona anropsmönster (menasync/awaitär nu att föredra)
I de flesta fall anropar du inte dessa metoder direkt. I stället använder du metodens anropssyntax för delegatvariabeln, som du ser i exemplen ovan. Men som du ser senare i den här serien finns det mönster som fungerar direkt med dessa metoder.
Sammanfattning
Nu när du har sett hur C#-språksyntaxen mappar till de underliggande .NET-klasserna är du redo att utforska hur starkt typade delegerade används, skapas och anropas i mer komplexa scenarier.