Delegates är ett sätt att kunna göra så att variabler pekar mot metoder istället för mot värden eller objekt i minnet. De är lite överkurs, men ganska användbara.
// Skapar en delegate som passar in på metoder som // inte tar emot några parametrar eller returnerar något.delegatevoidTask(); // En metod som passar in på delegatenstaticvoidSayHello() {System.WriteLine("Hello!");}staticvoidMain(string[] args){Task t = SayHello; // Lagra en referens till SayHello-metoden i "t"t(); // Att köra t som en metod är nu samma sak som att köra SayHello.}
När man skapar en delegat så beskriver den en metodprofil. Man kan säga att varje delegat beskriver en kategori av metoder.
// Stämmer in på metoder som tar emot en int-parameter och inte returnerar något.delegatevoidDelegateOne(int y);// Stämmer in på metoder som returnerar en int utan att ta emot några parametrar.delegateintDelegateTwo();// Stämmer in på metoder som returnerar en float efter att ha tagit emot// en string och en int.delegatefloatDelegateThree(string x,int y);
När man skapat sin delegat används den alltså som om den vore en datatyp. När man lagrar metoderna i den så ser man till att inte använda () efter metodnamnet, för då körs ju metoden istället, innan tilldelningen.
DelegateOne test = MethodOne;
Action
En Action är en generisk delegat som passar in på metoder som inte returnerar ett värde. Om man vill matcha metoder som tar emot parametrar kan dessa anges mellan <>.
Func är en generisk delegat som matchar metoder som returnerar något. En eller flera datatyper anges inom <>. Anges flera så är den sista alltid returtypen, resten är parametrar.
staticintAddition(int a,int b){return a + b;}staticstringMakeStrengthString(int str){return$"Strength: {str}";}Func<int,int,int> math = Addition;Func<int,string> statDisplay = MakeStrengthString;int y =math(3,4);string strength =statDisplay(14);
Anonyma metoder i delegatvariabler
Anonyma metoder saknar eget namn.
staticvoidMain(string[] args){Action t =delegate() {System.WriteLine("Hello!"); }t();}
De är praktiska när man aldrig faktiskt kommer att anropa metoden med dess eget namn, utan bara vill kunna lägga in den i en variabel eller en lista.
Om man vill att flera metoder ska köras när en delegat-variabel anropas så kan man kombinera delegater för att skapa s.k. multicast-delegater.
Action multiCaster =delegate() { Console.WriteLine("Hello"); };multiCaster +=delegate() { Console.WriteLine("World"); };multiCaster(); // Skriver först ut Hello, sedan World
Action good =delegate() { Console.WriteLine("Good"); };Action bye =delegate() { Console.WriteLine("Bye"); };Action morning =delegate() { Console.WriteLine("Morning"); };Action goodMorning = good + morning;Action goodBye = good + bye;goodMorning(); // Skriver ut "Good" och "Morning"goodBye(); // Skriver ut "Good" och "Bye"
Att lägga till en metod till en multicast-delegat kallas subscribing, och att ta bort en metod från en multicast-delegat kallas unsubscribing.
Events
Nackdelen med multicast-delegater är att den som har tillgång till dem inte bara kan lägga till nya metoder i dem, utan också aktivera dem och göra större ändringar – som att till exempel ändra dem till null.
Med en event kan den som har tillgång utifrån bara lägga till och ta bort metoder (subscribe/unsubscribe)
Events kan bara existera i klasser, och de kan bara anropas (invoke) inifrån den klassen
Avatar p =newAvatar();Action PauseGame =delegate() { Console.WriteLine("Game is paused"); };// Subscribe:a den lokala metoden PauseGame till eventet OnDeathp.OnDeath+= PauseGame;p.Update(); // Kör Update-metoden, som i sin tur invoke:ar eventet
Lambdas
Lambda-uttryck är, enkelt uttryckt, ett sätt att skriva väldigt enkla anonyma metoder (anonyma delegater) vars returvärden är direkta resultat av deras parametrar. Ett lambda-uttryck består av en parentes där den anonyma metodens parametervärden anges, en => och slutligen en enkel uträkning som motsvarar det som ska returneras från metoden.
Uträkningen kan bytas ut mot ett kodblock som returnerar ett värde, om mer omfattande
// Delegat som passar alla metoder som tar emot två int-parametrar och// som returnerar en int som resultatdelegateintCalculation(int x,int y);staticvoidMain(string[] args){ // Lambda-uttrycket => har två inputs på vänster sida, // och uträkningen på höger sida resulterar i en int. // Därför passar lambda-uttrycket in på delegaten Calculation.Calculation c = (xInput, xOutput) => xInput * xOutput;int result =c(10,5); // Detta gör samma sak som ovan, men med ett kodblock istället // för en ren beräkningCalculation c2 = (xInput, xOutput) => {return xInput * xOutput};}
Lambdas används väldigt ofta när man till exempel vill filtrera en lista på något sätt.
List<int> numbers =newList<int>() {2,3,4,5,6};// FindAll returnerar en lista med alla föremål i listan som matchar ett visst// kriterium. Kriteriet ska vara utformat som en metod, som tar emot ett// föremål av rätt datatyp som parameter och returnerar en bool.// lambda-uttrycket nedan tar emot en input på vänster sida (n) och uttrycket// på höger sida är en boolsk jämförelse.// Därför kommer lowNumbers att innehålla en lista med alla integers från// numbers, som är < 4.List<int> lowNumbers =numbers.FindAll(n => n <4);
De kan också användas till events och till multicast-delegates. Ofta används detta när det bara är ganska lite kod som ska köras.
Avatar p =newAvatar();p.OnDeath+= () =>Console.WriteLine("Game is paused");