miércoles, 18 de marzo de 2009

Predicado con parámetros o cómo poder pasar un parámetro al método Find de List

Predicados con parámetros

Una vez más un post que surge de una consulta cotidiana, un predicado es un tipo de delegado, o sea una firma de un método, en el caso del predicado genérico (que es el que nos interesa) la firma es la siguiente

public delegate bool Predicate<T>(T obj);

Entonces, lo que esto nos dice es que representamos un método que recibe T como parámetro y devuelve true o false, el objetivo de un predicado es que ese valor de retorno responda a una condición que nosotros consideramos verdadera, por ejemplo T fuese un tipo persona podriamos decir que será true el valor de retorno cuando su edad sea mayor de dieciocho años, para ser claro, un método que responda a esa regla y represente un predicado sería así:

private Predicate<Persona> PredicadoConParametro(string param)
{
    return new Predicate<Persona>(delegate(Persona target)
    {
        return target.Edad > 18;
    });
}

La duda es, si quiero que ese valor (18) sea parametrizable tengo que poner el predicado in-line para que "vea" el valor, así:

public void PredicadoInLine()
{
    List<Persona> personas = new List<Persona>()
    {
        new Persona{Nombre = "Enzo", Edad=36}, new Persona{Nombre = "Ariel", Edad=30}
    };

    int edad = 18;

    personas.Find(delegate(Persona target)
    {
        return target.Edad > edad;
    });
}      

Es claro que el problema es que si queremos tener el código del predicado en otro lado por los motivos que sea y necesitamos pasar un parámetro (en este caso la edad) no hay forma (con una variable global se puede..) entonces la solución sería poder tener un predicado con parámetros, bien, cómo se resuelve esto? vamos a analizar un poco

Lo que nos pide el mètodo Find es un predicado, nada nos impide poner un método que retorne un predicado, y a ese método pasarme un parámetro, en nuestro caso la solución sería así:

/// <summary>
/// Este método acepta un parámetro edad y devuelve un predicado, es lo que necesitamos 
/// para pasar al método Find de la lista
/// </summary>
private Predicate<Persona> PredicadoConParametro(int edad)
{
    return new Predicate<Persona>(delegate(Persona target)
    {
        return target.Edad > edad;
    });
}

public void Probar()
{
    //declaramos la lista con dos elementos
    List<Persona> personas = new List<Persona>()
    {
        new Persona{Nombre = "Enzo", Edad=36}, new Persona{Nombre = "Carlos", Edad=10}
    };

    //Verificamos que llamando al método Find de la lista utilizando nuestro predicado parametrizado nos
    //devuelva un elemento al menos
    Debug.Assert(personas.Find(PredicadoConParametro(18)) != null);
}

El método PerdicadoConParametro acepta un parámetro edad y devuelve un predicado que tiene en cuena esa edad, con lo cual cubrimos nuestro requerimiento. Mágico, hasta la próxima

2 comentarios:

Carlos dijo...

Considero que al ser un delegado una función "en linea" y en esencia tan simple que no debe ser re utilizable, no tiene sentido crear una función completa. Pues estas perdiendo el sentido real de esta funcionalidad

Leonardo Micheloni dijo...

Gracias por el comentario, si, es simple pero porque es un ejemplo :) sino como apuntás vos, no tendría sentido.