C# - Apresentando o delegate Predicate
Neste artigo eu vou apresentar os conceitos básicos envolvidos na utilização do delegado Predicate na linguagem C#. |
Para você poder acompanhar e entender este artigo você tem que saber o que é um delegate. Se você tem dúvidas leia o meu artigo : C# - Delegates e Eventos : Conceitos básicos e depois prossiga na leitura.
Eu não vou entrar em detalhes sobre o que é um delegate (para detalhes leia o artigo) vou apenas apresentar a sua definição (uma delas):
Um Delegate é um ponteiro para um método. Um Delegate pode ser passado como um parâmetro para um método. Podemos mudar a implementação do método dinamicamente em tempo de execução, a única coisa que precisamos para fazer isso seria manter o tipo de parâmetro e o tipo de retorno. |
Conceitos Básicos
1- O delegate Predicate
O Delegate Predicate representa o método que define um conjunto de critérios e determina se o objeto especificado atende os critérios.
O delegate Predicate<T> que usa parâmetro(s), executa o código usando os parâmetro(s) e sempre retorna um valor booleano.
Exemplo:
static void Main(string[] args) { // Este Predicate retorna true se o argumento for 1 Predicate<int> EUm = x => x == 1; // Predicate retorna true se o argumento for maior que 5 Predicate<int> EmaiorQueCinco = (int x) => x >= 5;
// Teste das intancias de Predicate com parâmetros |
Nestes exemplos eu estou usando expressões lambdas para definir o delegate.
Sintaxe:
public delegate bool Predicate<in T>(T obj )
Este parâmetro de tipo é contravariante. Ou seja, você pode usar o tipo especificado ou qualquer tipo que seja menos derivado. Para obter mais informações sobre covariância e contravariância, consulte Covariância e contravariância em genéricos.
Tipo:
System.Boolean
true se obj atende aos critérios definidos dentro do método
representado por esse delegado; caso contrário, false.
Este delegado é usado por vários métodos das classes de Array e de List<T> para pesquisar os elementos na coleção.
Normalmente, o delegate Predicate<T> é representado por uma expressão de lambda.
Como as variáveis com escopo local estão disponíveis para a expressão de lambda, é fácil testar uma condição que não é conhecida precisamente em tempo de compilação.
Usando o delegate Predicate<T>
No exemplo a seguir temos um exemplo clássico de uso do delegate Predicate<T> para encontrar um
número interiro em um array de número que atende a um determinado critério.
No exemplo o número deve ser maior que 7000.
Exemplo :
using System;
namespace Delegate_Predicate
{
class Program
{
static void Main(string[] args)
{
// Cria um array de números inteiros
int[] numeros = { 2343, 5349, 6039, 4326, 3038, 1987, 8762, 1098 };
// Define o delegato Predicate<T>
Predicate<int> predicate = EncontraNumeros;
// Encontra o numero que é maior que 7000
int numero = Array.Find(numeros, predicate);
// exibe o primeiro numero encontrado que atente o critério
Console.WriteLine("Encontrado: n = {0} ", numero);
Console.ReadKey();
}
//função que retorna true (n1>7000) / false (n1<=7000)
private static bool EncontraNumeros(int n1)
{
return n1 > 7000;
}
}
}
|
Vejamos o mesmo exemplo acima usando uma expressão lambda para representar o delegate Predicate<T> :
using System;
namespace Delegate_Predicate
// Encontra o numero que é maior que 7000 // exibe o primeiro numero encontrado que atente o criterio |
Cada elemento do array de números é passado para a expressão de lambda até que a expressão localize um elemento que atende aos critérios de pesquisa. Nesse caso, a expressão de lambda retorna true se o número for maior que 7000.
Note como o código ficou mais conciso usando a expressão lambda.
Então podemos simplificar muito o código no uso dos delegates Predicate<T> usando expressões lambadas. Veja abaixo um exemplo que mostra a evolução desde o código tradicional, o uso do delegate e finalmente da expressão lambada:
Para este exemplo vamos criar uma classe Inventario :
public class Inventario { public int Id { get; set; } public int Estoque { get; set; } public double Preco { get; set; } } |
E vamos criar uma lista de itens no inventário :
public static List<Inventario> CriaListaInventario() { List<Inventario> listaInventario = new List<Inventario>(); listaInventario.Add(new Inventario { Id = 1, Estoque = 5, Preco = 1.99 }); listaInventario.Add(new Inventario { Id = 2, Estoque = 5, Preco = 2.09 }); listaInventario.Add(new Inventario { Id = 3, Estoque = 5, Preco = 0.75 }); listaInventario.Add(new Inventario { Id = 4, Estoque = 5, Preco = 4.25 }); listaInventario.Add(new Inventario { Id = 5, Estoque = 5, Preco = 5.60 }); listaInventario.Add(new Inventario { Id = 6, Estoque = 5, Preco = 2.15 }); return listaInventario; } |
A seguir vamos definir um código para localizar itens no inventário usando o código tradicional, o delegate Predicate<T>, e simplificar o código uma usando uma expressão lambda. Veja como ficou:
public Inventario
LocalizaInventario(int inventarioId) { foreach (Inventario item in listaInventario) { if (item.Id == inventarioId) return item; } return null; } |
Este é o código
tradicional que pode ser usado para localizar item na lista de inventário. Usamos o laço foreach e percorremos a lista verificando se o código do item informado esta na lista. |
public static Inventario
LocalizaInventario(int inventarioId) { Predicate<Inventario> pred = delegate(Inventario item) { return item.Id == inventarioId; }; return listaInventario.Find(pred); } |
Podemos melhorar
um pouco usando o método Find da classe List<T>. Aqui o argumento do método Find é o delegate Predicate<T> Aqui temos um delegate inline que cria um método anônimo e pode ser atribuído à variável Predicate que é passado ao método Find. |
public static Inventario
LocalizaInventario(int inventarioId) { Predicate<Inventario> pred = item => item.Id ==inventarioId; return listaInventario.Find(pred); } |
Podemos
simplificar ainda mais passando usando uma expressão lambda. Aqui a expressão lambda esta sendo atribuída ao delegate Predicate pred que pode ser reutilizado. |
public static Inventario
LocalizaInventario(int inventarioId) { return listaInventario.Find(item => item.Id == inventarioId); } |
Podemos simplificar ainda mais ignorando a variável Predicate pred em favor da utilização da expressão lambda no método Find. |
Vimos então que o tipo Predicate na linguagem C# armazena um método que recebe um parâmetro e retorna um valor de verdadeiro ou falso. Assim, um delegate Predicate retorna sempre verdadeiro ou falso.
A seguir temos outros métodos de List<T> que também usam o tipo Predicate como argumento :
Para ver a lista completa, consulte http://msdn.microsoft.com/en-us/library/s6hkc2c4.aspx
Pegue o exemplo do projeto aqui: Delegate_Predicate.zip
Porque os judeus pedem
sinal, e os gregos buscam sabedoria;
Mas nós pregamos a Cristo crucificado, que é escândalo para os judeus, e loucura
para os gregos.
Mas para os que são chamados, tanto judeus como gregos, lhes pregamos a Cristo,
poder de Deus, e sabedoria de Deus.
1 Coríntios 1:22-24
Referências: