W kolejnym wpisie o linq przedstawię zasadę działania Func, Predicate, Action.
Dla przypomnienia Linq jest połączeniem 5 elementów:
- extension methods
- yield
- Func, Predicate, Action
- wyrażenia lambda
- IQueryable i Expression Trees
Przyjrzyjmy się najpierw deklaracji tych trzech typów.
1 2 3 4 5 6 7 8 9 |
public delegate void Action<in T>(T obj); public delegate bool Predicate<in T>( T obj ) public delegate TResult Func<in T, out TResult>( T arg ) |
Z definicji tych wynika, ze powyższe typy to nic innego jak delegaty. Do każdego z tych typów możemy przypisać metodę statyczna, delegata anonimowego lub wyrażenie lambda.
W tym wpisie podam przykłady jak zastosować pierwsze dwa przypadki. Wyrażenia lambda zostaną omówione w kolejnym wpisie.
Action
Z deklaracji tego typu wynika ze możemy tutaj przypisać metodę, która przyjmuje jeden parametr i nie zwraca żadnego wyniku. Action posiada wiele wersji, różniących się ilością przyjmowanych parametrów aż do 16.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class Actions { public static void TestAction() { const string testString = "Not only .Net"; //do tak zadekarowanej akcji można przypisać metodę przyjmujacą string Action<string> someAction = WordCount; someAction(testString); //ta sama metoda zapisana w postaci delegata anonimowego someAction = delegate(string s) { Console.WriteLine("Ilość słów: {0}", s.Split(' ').Length); }; someAction(testString); } private static void WordCount(string s) { Console.WriteLine("Ilość słów: {0}",s.Split(' ').Length); } } |
Predicate
Do predicate możemy dopisać metodę przyjmującą jeden parametr i zwracającą bool. Nie istnieje żadne przeładowanie tego typu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class Predicats { public static void TestPredicats() { var r = new Random(); int x = r.Next(); Predicate<int> somePredicate = IsModTwo; Console.WriteLine("Liczba {0} {1} podzielna przez 2", x, somePredicate(x) ? "jest " : "nie jest"); //ten sam predykat zapisany za pomocą delegata anonimowego somePredicate = delegate(int i) { return i % 2 == 0; }; Console.WriteLine("Liczba {0} {1} podzielna przez 2", x, somePredicate(x) ? "jest " : "nie jest"); } private static bool IsModTwo(int i) { return i % 2 == 0; } } |
Func
Func podobnie jak action ma wiele przeładowań, różni się tym że ostatni parametr generyczny definiuje typ jaki metoda powinna zwracać.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class Funcs { public static void TestFuncs() { const string testString = "Not only .Net"; //deklaracja metody przyjmującej stringa a zwracającej inta Func<string,int> someFunc = SomeFunc; Console.WriteLine("Ilość słów {0}", someFunc(testString)); someFunc = delegate(string s) { return s.Split(' ').Length; }; Console.WriteLine("Ilość słów {0}", someFunc(testString)); } private static int SomeFunc(string s) { return s.Split(' ').Length; } } |
Ok, ale jak to się ma do Linq, popatrzmy na poniższą deklaracje metody TakeWhile, która wchodzi w skład Linq.
1 2 3 4 5 6 7 8 9 10 |
public static IEnumerable<T> TakeWhile<T>(this IEnumerable<T> source, Func<T, bool> predicate) { foreach (T source1 in source) { if (predicate(source1)) yield return source1; else break; } } |
Jak widać jest to metoda, która rozszerza kolekcje i przyjmuje jako parametr Func. Func w tym przypadku ma taką samą deklaracje jak predykat, czyli zwraca bool. Metoda TakeWhile, wykorzystuje yield i dopóki func jest spelniony będzie zwracała kolejny element kolekcji.
Poniższy przykład iteruje po kolekcji, tak długo jak jej elementy są parzyste.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Program { static void Main(string[] args) { var list = new List<int> {2, 4, 6, 8, 5, 10}; Func<int,bool> isModTwo = delegate(int i) { return i%2 == 0; }; foreach (var x in list.TakeWhile(isModTwo)) { Console.WriteLine(x); } } } |
2 Komentarze
Grzesiek
19 sierpnia 2013 na 12:41 (UTC 2) Link do tego komentarza
Nie kumam tego zdania
Brakuje chyba kropki pomiędzy “przypadki” a “wyrażenia”.
graf
19 sierpnia 2013 na 12:51 (UTC 2) Link do tego komentarza
Ok poprawione.