Delegater, events och lambdauttryck Läs avsnitt 5.13 Delegater, i kursboken och Delegater, https://msdn.microsoft.com/en-us/library/ms173172.aspx Events, https://msdn.microsoft.com/en-us/library/awbftdfh.aspx Lambdauttryck, https://msdn.microsoft.com/en-us/library/bb397687.aspx 1
Delegater, ungefär som funktionspekare... En delegat är en lista med hänvisningar till metoder Det går att anropa metoderna via delegaten Endast den sist inlagda metodens returvärde returneras via en delegat Definition av en delegattyp delegate returtyp Typnamn(parameterlista) Deklaration samt deklaration och initiering av en delegat Typnamn delegatensnamn; Typnamn delegatensnamn = EnMetod; Typnamn delegetensnamn = new Typnamn(EnMetod); //Gamla sättet Lägga till flera metoder delegatensnamn += EnMetod delegetensnamn += new Typnamn(EnMetod) Ta bort metoder delegatensnamn -= EnMetod delegatensnamn -= new Typnamn(EnMetod) 2
Att sända med metoder som parametrar public delegate double AritmetiskFunktion(double etttal, double ettannattal); public class RäkneDosa public void Beräkna(AritmetiskFunktion delegattillenmetod, double tal1, double tal2) double resultatet = delegattillenmetod(tal1, tal2); Console.WriteLine("Resultatet blir: " + resultatet); public class Program public static double Plus(double tala, double talb)return tala + talb; public static double Minus(double tala, double talb)return tala - talb; public static void Main() RäkneDosa räknedosan = new RäkneDosa(); räknedosan.beräkna(plus, 1, 2); räknedosan.beräkna(minus, 1, 2); AritmetiskFunktion gånger = delegate(double tal1, double tal2) return tal1 * tal2; ; räknedosan.beräkna(gånger, 1, 2); räknedosan.beräkna(delegate(double tal1, double tal2)return tal1 / tal2;, 1, 2); Resultatet blir: 3 Resultatet blir: -1 Resultatet blir: 2 Resultatet blir: 0,5 3
En delegat fungerar som en lista... public class Texter public void Välkommen() Console.WriteLine("Hej på dig!"); public void Fråga() Console.WriteLine("Hur står det till?"); public static void Avsked() Console.WriteLine("Hej då!"); public delegate void InfoTyp(); public class Anslagstavla private InfoTyp InfoListan; public void LäggTillInfo(InfoTyp meddelande) InfoListan += meddelande; public class Program public static void Main() Texter textrad = new Texter(); Anslagstavla tavlan = new Anslagstavla(); public void VisaInfo() InfoListan(); tavlan.läggtillinfo(textrad.välkommen); tavlan.läggtillinfo(textrad.fråga); tavlan.läggtillinfo(texter.avsked); tavlan.visainfo(); Hej på dig! Hur står det till? Hej då! 4
Det blir enklare med ett event public class Texter public void Välkommen() Console.WriteLine("Hej på dig!"); public void Fråga() Console.WriteLine("Hur står det till?"); public static void Avsked() Console.WriteLine("Hej då!"); public delegate void InfoTyp(); public class Anslagstavla public event InfoTyp InfoListan; public void VisaInfo() InfoListan(); Ett event är en delegat som endast kan förekomma till vänster om += och -= utanför den klass i vilken den är deklarerad. Därför är det ingen fara att deklarera den som publik i de flesta sammanhang. public class Program public static void Main() Texter textrad = new Texter(); Anslagstavla tavlan = new Anslagstavla(); tavlan.infolistan += textrad.välkommen; tavlan.infolistan += textrad.fråga; tavlan.infolistan += Texter.Avsked; tavlan.visainfo(); Hej på dig! Hur står det till? Hej då! 5
Action och Func I namnrymden System finns två olika uppsättningar av redan färdigdefinierade typer av delegater, Action och Func Delegater av typen Action kan hänvisa till metoder som returnerar inget, men kan ha upp till 16 parametrar Definitionen av en Action-delegat är lite omständig, ett exempel med två parametrar: public delegate void Action<in T1, in T2>( T1 arg1, T2 arg2 ) Action-delegater: läs mera, https://msdn.microsoft.com/en-us/library/system.action(v=vs.110).aspx Delegater av typen Func kan hänvisa till metoder som har ett returvärde och upp till 16 parametrar Ett exempel på definitionen av en Func-delegat med två parametrar och en returtyp: public delegate TResult Func<in T1, in T2, out TResult>( T1 arg1, T2 arg2 ) Func-delegater: läs mera, https://msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx 6
Räknedosan med hjälp av Func-delegater public class RäkneDosa public void Beräkna(Func<double, double, double> EnMetod, double tal1, double tal2) double resultatet = EnMetod(tal1, tal2); Console.WriteLine("Resultatet blir: " + resultatet); Returtypen kommer alltid sist public class Program public static double Plus(double tala, double talb)return tala + talb; public static double Minus(double tala, double talb)return tala - talb; public static void Main() RäkneDosa räknedosan = new RäkneDosa(); räknedosan.beräkna(plus, 1, 2); räknedosan.beräkna(minus, 1, 2); Resultatet blir: 3 Resultatet blir: -1 Resultatet blir: 2 Resultatet blir: 0,5 Func<double, double, double> gånger = delegate(double tal1, double tal2) return tal1 * tal2; ; räknedosan.beräkna(gånger, 1, 2); räknedosan.beräkna(delegate(double tal1, double tal2)return tal1 / tal2;, 1, 2); 7
Anslagstavlan med hjälp av en Action-delegat public class Texter public void Välkommen() Console.WriteLine("Hej på dig!"); public void Fråga() Console.WriteLine("Hur står det till?"); public static void Avsked() Console.WriteLine("Hej då!"); public class Anslagstavla public event Action InfoListan; public void VisaInfo() InfoListan(); public class Program public static void Main() Texter textrad = new Texter(); Anslagstavla tavlan = new Anslagstavla(); tavlan.infolistan += textrad.välkommen; tavlan.infolistan += textrad.fråga; tavlan.infolistan += Texter.Avsked; tavlan.visainfo(); Hej på dig! Hur står det till? Hej då! 8
EventHandler I namnrymden System finns delgaten EventHandler definierad public delegate void EventHandler<TEventArgs>( object sender, TEventArgs e ) Se, https://msdn.microsoft.com/en-us/library/db0etb8x(v=vs.110).aspx Metoden som EventHandler hänvisar till måste ha en parameterlista bestående av parametrarna object sender och TEventArgs e object sender, ska referera till det objekt som avfyrade eventet TEventArgs e, e är en referens till ett objekt som kan innehålla extra information om det event avfyras. Klassen för ett sådant objekt får du skapa själv. Den klassen bör ärva från klassen System.EventArgs. Om du inte har någon extra information som ska bifogas det event som avfyras kan du i stället sända med EventArgs.Empty 9
Observer-mönstret Mönstret består av en förvaltare som har information och en eller flera observatörer som återger förvaltarens information Mönstret garanterar att observatörerna automatiskt blir underrättade om förändringar av förvaltarens information För att det ska vara möjligt måste varje informatör prenumerera på förvaltarens tjänst om att bli underrättad av observatören I exemplet Observer beskriver klassen Dokumenten förvaltare av information, i det här fallet ett textmeddelande beskriver klasserna Prenumerant, VisaInfo och VisaStatistik observatörer Dokument-objektet avfyrar ett event av typen EventHandler varje gång dess meddelande har förändrats Vilken metod som ska anropas i varje observatör bestämmer observatörerna själva när registerar sig till prenumerationen av tjänsten. I exemplet ListBoxExempel02 tillämpas observer-mönstret Observatören är klassen DataSource. Därför kan vi inte se vilken metod som event PropertyChanged är kopplad till. Vi kan enbart avfyra eventet så snart som en persons information har förändrats En instans av DataSource hittar du i verktygslådan för grafiska komponenter. Det är bara att dra in den i det fönster där den behövs. 10
Lambdauttryck Lambdauttryck är ett förenklat skrivsätt för anonyma metoder Lambdauttryck börjar med en parameterlista inom ( ) => markerar att lambdauttryckets kropp följer, kroppen markeras med I lambdauttryckets kropp kan man lägga vanliga satser // Här definieras en anonym metod Funktion summan = delegate (double tal1, double tal2) return tal1 + tal2; ; dosan.beräkna(summan, 1, 2); // Här skapas en anonym metod med hjälp av ett lambdauttryck Funktion skillnaden = (double tal1, double tal2) => return tal1 - tal2; ; dosan.beräkna(skillnaden, 1, 2); // Man behöver inte deklarera parametrarnas typer i ett lambdauttryck Funktion gånger = (tal1, tal2) => return tal1 * tal2; ; dosan.beräkna(gånger, 1, 2); // Här definieras ett lambdauttryck direkt i anropet till metoden Beräkna dosan.beräkna((tal1, tal2) => return tal1 / tal2;, 1, 2); 11
Lambdauttryck och FCL Många metoder som kräver någon typ av extra information om storleksordning, giltigt interval för värden eller liknande, kan ofta ta lambdauttryck Person[] personer = ; new Person("Kalle", 30), new Person("Zeke", 10), new Person("Kajsa", 50), new Person("Jan", 27) Array.Sort(personer, (persona, personb) => return persona.namn.compareto(personb.namn); ); List<Person> listan = new List<Person>(personer); listan.sort((persona, personb) => ); if (persona.ålder < personb.ålder) return -1; else if (persona.ålder == personb.ålder) return 0; else return 1; 12
Restriction Operators För de flesta collections kan man göra olika urval och beräkningar av de objekt som är lagrade Det finns olika operatorer i form av tilläggsmetoder för collections i namnrymden System.Linq Operateratorena är beroende av selectors eller predicates. Dessa beskriver hur de värden som operatorerna använder ska betraktas eller behandlas. Predicate och selectors kan beskrivas med hjälp av lambda-uttryck Ett predicate, ska returnera ett värde av typen bool En selector, kan returnerar en valfri typ av värde Exempel på operatorer med predecates är, Count, Single, While Exempel på operatorer med selectorer är, Max, Min, Select Se The.NET Standard Query Operators https://msdn.microsoft.com/en-us/library/bb394939.aspx 13
Mera om lambdauttryck Man behöver inte markera lambdautryckets parameterlista med ( ) och dess kropp med samt använda return, förutsatt att inga tvetydigheter uppstår. Metoden Where finns i namnrymden System.Linq. Ordet var markerar att typen ska väljas implicit. Ofta blir IEnumerable vald för samlingar av värden. var jämnaår = personer.where( x => x.ålder % 10 == 0 ); skriv("personer som har fyllt jämnt i år", jämnaår); Metoden skriv skriver ut en samling med personer. foreach-loopar fungerar enbart med samlingar som implementerar IEnumerable. Arrayer och alla typer av Container-classer gör det. private static void skriv(string text, IEnumerable<Person> personlista) Console.WriteLine(text); foreach (Person person in personlista) Console.WriteLine(person); Console.WriteLine(); 14