Predicate to gotowy szablon delegaty/wskaźnika do metody.
Metoda referowana w Predicate musi zwrócić wartość true/false.
Spełnia ona inną role niż klasyczne szablony delegat jak “Action” i “Func”, które opisałem wcześniej. Pytania brzmi na czym polega różnica.
Możesz sobie zadać pytanie dlaczego w ogóle z nich korzystać. Dla czytelności systemu może łatwiej jest użyć delegaty Func<T,Bool>. Dlaczego Predicate w ogóle istnieje?
class Program
{
static void Main(string[] args)
{
Program p = new Program();
Func<bool> check = p.Check;
Func<int,bool> checkIf = p.CheckIfNumberMoreThan10;
//Predicate<> bez parametrow nie istnieje
Predicate<int> checkif = p.CheckIfNumberMoreThan10;
}
public bool Check()
{
return true;
}
public bool CheckIfNumberMoreThan10(int n)
{
return n > 10;
}
}
Otóż istnieje różnica pomiędzy nimi.
Pierwsza rzecz, która rzuca się w oczy to fakt, że Predicate przyjmuje tylko jeden parametr określający jaki 1 argument ma przyjąć metoda.
Jeśli więc mamy metodę która przyjmuje więcej parametrów to warto skorzystać z delegaty Func.
To oczywiście zadaje ponownie inne ważne pytanie “po co ją w ogóle stosować”?
Odpowiedź na pytanie czy w ogóle używać delegaty Predicate leży w samym frameworku .NET. Sam framework .NET najczęściej korzysta z delegaty Func.
Oto przykłady z LINQ;
Przy projekcji typów.
list.Select(x => x.Property)
Przy filtrowaniu obiektów.
list.Where(x => x.Value == otherValue)
Przy scalaniu dwóch kolekcji swoim kluczami.
list.Join(otherList, x => x.FirstKey, y => y.SecondKey, ...)
Gdzie jest więc używany Predicate.
W .NET 2.0 był on używany do znajdowania elementów w kolekcjach. W czasach gdy nie było jeszcze LINQ.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> list = new List<int> { 11, 2, 13, 9, 22 };
Predicate<int> predicate = new Predicate<int>(greaterThanTen);
List<int> newList = list.FindAll(predicate);
}
static bool greaterThanTen(int arg)
{
return arg > 10;
}
}
Widać wyraźnie, że to był wtedy Trend. Wszystkie metody Find* dla kolekcji generycznych w tym wypadku listy przyjmują delegatę Predicate. Tak samo jest z metodami “Remove*”.
W C# 2.0 delegata Predicate była deklarowana najczęściej w taki sposób przy użyciu anonimowych metod.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> list = new List<int> { 14, 33, 13 };
List<int> newList = list.FindAll(delegate(int number)
{
return number > 10;
});
}
}
Teraz gdy C# 3.0 i Linq istnieje ten sam kod można napisać dużo lepiej. Nie jawnie użyć delegaty Predicate przy pomocy wyrażenia Lambda.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> list = new List<int> { 4, 55, 33, 11, 7, 8, 31 , 12 };
List<int> newList = list.FindAll(i => i > 10);
}
}
Oczywiście w .NET 3.5 gdzie Linq zostało zaprezentowane do dyspozycji mam metodę Where, która spełnia ten sam cel.
List<int> list = new List<int> { 4, 55, 33, 11, 7, 8, 31, 12 };
var listA = list.Where(n => n > 10).ToList();
Wszystko więc wskazuje na to, że delgata Predicate i metody takie jak FindAll istnieją tylko po to by kod był kompatybilny wstecz.
Nie istnieje więc powód by stosować tą delegatę w swoim kodzie.
Predicate jest specjalnym przykładem Func<T,bool> i było on potrzebny ponieważ delegaty jak Func wtedy nie istniały.
Zapewne gdyby delegaty takie jak Action, Func zostały przedstawione wcześniej Predicate w ogóle by nie istniał.
Nie ma sensu mieć dwie delegaty, które spełniają taką sama funkcję.
Nawet jeśli stworzy kod, który ma iterować po kolekcji według delegaty Predicate.
class Program
{
static void Main(string[] args)
{
Program p = new Program();
Predicate<Person> predicatePointer = p.MoreThan20;
Person[] lstPeople = (new Person[]
{
new Person(){ Name = "Tomek", Age = 23},
new Person(){ Name = "Franko", Age = 21},
new Person(){ Name = "Doman", Age = 18},
new Person(){ Name = "Alicja", Age = 19},
});
Person tempPerson = Array.Find(lstPeople, predicatePointer);
Console.WriteLine("Person older than 22 is :" + tempPerson.Name);
Console.ReadKey();
}
public bool MoreThan22(Person employee)
{
return employee.Age > 22;
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Obecnie zawsze może użyć metody Where.
class Program
{
static void Main(string[] args)
{
Program p = new Program();
Func<Person,bool> predicatePointer = p.MoreThan20;
Person[] lstPeople = (new Person[]
{
new Person(){ Name = "Tomek", Age = 23},
new Person(){ Name = "Franko", Age = 21},
new Person(){ Name = "Doman", Age = 18},
new Person(){ Name = "Alicja", Age = 19},
});
Person tempPerson = lstPeople.Where(predicatePointer).First();
Console.WriteLine("Person older than 22 is :" + tempPerson.Name);
Console.ReadKey();
}
public bool MoreThan20(Person employee)
{
return employee.Age > 22;
}
}
To wszystko co musisz wiedzieć na temat delegaty Predicate.