Advanced Services | Wichtige Delegates aus der API |
Es gibt mehr als 70 Delegates in der API. Einige wichtige sollen hier erwähnt werden.
Das Action Delegate gibt es mittlerweile in 17 Varianten, einmal nicht generisch und 16 mal generisch, es hat die folgende Signatur(en).
public delegate void Action(Object obj) public delegate void Action<in T>(T obj) public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2) public delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3) ... public delegate void Action<in T1, in T2, in T3, in T4, ..., in T16>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, ..., T16 arg16)
Parameter, die mit in gekennzeichnet sind, sind die normalen Parameter der Parameterliste.
Das Delegate wird u.a. verwendet in der ForEach()-Methode, die eine foreach-Schleife ersetzen kann. Die ForEach()-Methode wiederum gibt es als statische Methode in der Klasse Array und als nichtstatische Methode in der Klasse List<T>. Die folgenden Codeschnipsel zeigen die Verwendung.
int[] intArr = { 1, 3, 5, 7, 9, 8, 6, 4, 2 }; Array.Sort(intArr); Array.ForEach(intArr, i => Console.Write(i + " ")); Console.WriteLine();
Das gleiche mit Strings.
string[] stringArr = { "eins", "zweins", "dreins", "vierns", "fünfs", "sächs"}; Array.Sort(stringArr); Array.ForEach(stringArr, st => Console.Write(st + " ")); Console.WriteLine();
Screenshot
Ganz ähnlich geht es auch mit der Klasse List
int[] intArr = { 1, 3, 5, 7, 9, 8, 6, 4, 2 }; List<int> intList = intArr.OfType<int>().ToList(); intList.Sort(); intList.ForEach( i => Console.Write(i + " ") ); Console.WriteLine();
Mit Strings
string[] stringArr = { "eins", "zweins", "dreins", "vierns", "fünfs", "sächs" }; List<string> stringList = new List<string>(stringArr); stringList.Sort(); stringList.ForEach(st => Console.Write(st + " "));
Wir bauen das vorige Beispiel aus und verwende dasComparison-Delegate mit der Methode Sort die es wiederum in den Klassen Array und List gibt. Wir wollen das Delegate dazu verwenden, die Daten abwärts zu sortieren.
Zunächst für Arrays vom Typ int
Array.Sort( intArr, (i, j) => j - i ); // abwärts sortieren Array.ForEach(intArr, i => Console.Write(i + " ") ); Console.WriteLine();
und vom Typ string.
Array.Sort(stringArr, (s, t) => t.CompareTo(s) ); // abwärts sortieren Array.ForEach(stringArr, st => Console.Write(st + " ") ); Console.WriteLine();
Nun für Listen vom Typ int
intList.Sort( (i, j) => j - i ); // abwärts sortieren intList.ForEach(i => Console.Write(i + " ")); Console.WriteLine();
und vom Typ string.
stringList.Sort( (s, t) => t.CompareTo(s) ); // abwärts sortieren stringList.ForEach(st => Console.Write(st + " ")); Console.WriteLine();
Screenshot
Dieses Delegate findet Verwendung in der Methode ConvertAll , die wiederum sowohl in Array und List auftaucht, einmal statisch und einmal nicht statisch. Wir wollen sie zunächst verwenden um aus einem Array von strings ein Array von double zu erzeugen.
Wir wollen die Methode zunächst verwenden, um aus einem Array von strings ein Array von double zu erzeugen und dieses dann mit Hilfe einer Action auszugeben. Mit Lambdaausdrücken brauchen wir dafür nur wenige Zeilen.
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); // System.Globalization string[] stringArr = {"7.34", "1.65", "21.02", "12.55", "3.45" }; double[] doubleArr = Array.ConvertAll(stringArr, st => Convert.ToDouble(st)); Array.Sort(doubleArr); Array.ForEach(doubleArr, d => Console.Write("{0} ", d)); Console.WriteLine();
Nun ganz ähnlich für eine Liste. Wir verwenden ein wenig Reflection. Ein Assembly ist eine Sammlung von Klassen und Interfaces. Wir wollen alle Typen aus dem Assembly mscorlib, es enthält 2781 Klassen aus der API in complilierter Form. Aus der Liste von Typen machen wir eine Liste von strings.
Assembly mscorlib = Assembly.Load("mscorlib"); // System.Reflection List<Type> mscorlibTypes = mscorlib.GetTypes().ToList(); List<string> mscorlibStrings = mscorlibTypes.ConvertAll(t => t.Name);
Wir filtern mit einem Predicate diejenigen Klassen heraus, deren Name mit 'Q' beginnt.
mscorlibStrings.Sort(); List<string> startsWithQ = mscorlibStrings.FindAll(s => s.StartsWith("Q")); startsWithQ.ForEach(t => Console.WriteLine(t)); Console.WriteLine();
Das Predicate-Delegate wird meistens verwendet eine Sammlung von Daten nach einem bestimmten Kriterum zu filtern.
Wir wollen aus einem string-Array bestimmte Elemente herausfiltern. Erstmal suchen wir alle strings, der zweiter Buchstabe ein 'a' ist unabhängig von der Groß- und Kleinschreibung.
string[] stArr = { "abc", "acb", "bac", "bca", "cab", "cba", "ABC", "ACB", "BAC", "BCA", "CAB", "CBA" }; string[] found = Array.FindAll(stArr, s => s.ToLower()[1] == 'a'); Array.ForEach(found, s => Console.Write(s + " ")); Console.WriteLine();
Nun suchen wir alle strings, die einen Umlaut enthalten, wiederum unabhängig von der Groß- und Kleinschreibung.
string[] stringArr = { "eins", "zweins", "dreins", "vierns", "fünfs", "sächs", "süben", "zän", "ölf" }; string[] found2 = Array.FindAll(stringArr, s => s.ToLower().Contains('ä') || s.ToLower().Contains('ö') || s.ToLower().Contains('ü') ); Array.ForEach(found2, s => Console.Write(s + " ")); Console.WriteLine();
Screenshot für beide Codeschnipsel.
Das Func Delegate gibt es mittlerweile in 16 Varianten, es hat die folgende Signatur(en).
public delegate TResult Func<out TResult>() public delegate TResult Func<in T, out TResult>(T arg) public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2) ... public delegate TResult Func<in T1, in T2, in T3, ..., in T16, out TResult>(T1 arg1, T2 arg2, T3 arg3, ..., T16 arg16)
Parameter, die mit in gekennzeichnet sind, sind die normalen Parameter der Parameterliste, der mit out gekennzeichnete Parameter ist der Returntyp bzw. Returnwert.
Wir arbeiten mit der Erweiterungsmethode Where die zu einem beliebigen Arraytyp existiert, diesmal in nichtstatischer Form.
string[] stringArr = { "eins", "zweins", "dreins", "vierns", "fünfs", "sächs", "süben", "zän" }; //////////////////////////////////////////////////////////////////////////// IEnumerable<string> result = stringArr.Where( s => s[0] >= 's' ); //////////////////////////////////////////////////////////////////////////// Console.WriteLine(); result.ToList().ForEach(t => Console.Write(t + " ")); Console.WriteLine(); Console.WriteLine();
Es sieht hier so aus, als ob der Ausdruck in der Where-methode ein Predicate wäre, das stimmt aber nur von der Wirkung her, aber nicht vom Typ, was die folgenden Zeilen beweisen.
Predicate<string> pre = s => s[0] >= 's'; IEnumerable<string> result = stringArr.Where(pre);
Die zweite Zeile erzeugt jedoch die Fehlermeldung
cannot convert from 'Predicate<string>' to 'Func<string,int,bool>'
Hier bekommen wir einen Hinweis auf den verlangten Typ, also:
Func<string, bool> func = s => s[0] >= 's'; IEnumerable<string> result = stringArr.Where(func);
Die beiden Delegates sind auch nicht zuweisungskompatibel.