Mostrando entradas con la etiqueta predicados. Mostrar todas las entradas
Mostrando entradas con la etiqueta predicados. Mostrar todas las entradas

jueves, 2 de agosto de 2007

Introducción a Delegados, métodos anónimos, Predicados, etc.

Introducción

Los delegados son una característica muy interesante del framework que nos permiten pasar métodos como parámetros de otros métodos, extraño, puede ser, pero es menos complicado de lo que parece. En el framework 2 nos podemos encontrar con los delegados por todas partes, en diferentes formas, como delegados, como predicados, como acciones.

¿Qué es un delegado?

Como dije antes un delegado nos permite pasar un método como parámetro de otro método, ok, sabemos cómo hacer para pasar tipos de datos como parámetros, simplemente declaramos en tipo y el nombre, detengámonos un momento, si yo quisiera indicar que en cierto lugar espero un método para utilizarlo qué cosas tengo que establecer. La respuesta es clara, el tipo de retorno del método y los parámetros que acepta, bueno, esto es justamente un delegado, una firma que nos permite establecer cómo deberá ser el método que esperamos.

Ejemplo 1

Vamos a ver la declaración de un delegado para utlizarlo en un método nuestro como parámetro.

   1:  public delegate void MiDelegado(string mensaje);

Es muy claro que si quitamos la palabra delegate de esta declaración es idéntica a una declaración de un método cualquiera. Ahora vamos a crear un método que espere un delegado del tipo MiDelegado para invocar el método dentro.

   1:          public void TestDelegado(MiDelegado delegado)
   2:          {
   3:              delegado("hola delegado loco");
   4:          }


Ok, este método va a tomar como parámetro un delegado del tipo MiDelegado (o sea un método que devuelva void y tome un string como parámetro) y luego lo va a invocar. Veamos la llamada al método.
Primero creamos el método que utlizaremos como parámetro del tipo MiDelegado.


   1:  public void MetodoDelegado(string mensaje)
   2:  {
   3:          Console.WriteLine(mensaje);
   4:  }

Sencillo, el método recibe un string lo escribe en la consola y no devuelve nada. Ahora vamos a ver cómo se invoca.

   1:  private void Test()
   2:  {
   3:          MiDelegado delegado = new MiDelegado(MetodoDelegado);
   4:          TestDelegado(delegado);
   5:  }


Más sencillo aún, lo corremos y vemos que funciona.




Vemos que en la línea 3 declaramos el delegado y le pasamos como parámetro el método que cumple con el delegado, esto lo podríamos haber resumido así


   1:  TestDelegado(new MiDelegado(delegate(string message)
   2:  {
   3:      Console.WriteLine(message);
   4:  }));

Lo que hacemos aquí es definir en la misma línea el delegado y la implementación, esto es lo que se conoce como método anónimo. Es un método anónimo justamente porque no tiene nombre, si bien a simple vista puede parecer que el código se pone confuso, en muchas ocasiones es más práctico utilizar un método anónimo.

¿Y qué es un predicado?

Si utilizaron listas genéricas se habrán encontrado con que algunos método, como Find por ejemplo, toman como parámetro un predicado, bien, esto no es más que un delegado como el que acabamos de crear pero con una firma diferente, vamos a ver la definición,

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


Está claro que es un delegado que define un método que acepte un tipo T (cualquiera) y que retorne bool. Esto es para indicar que el objeto que pasa como parámetro cumple con el criterio de búsqueda, vamos a ver el método Find de la lista genérica. Echamos mano del Reflector para variar y vemos esto:

   1:  public T Find(Predicate match) 
   2:  {
   3:      if (match == null)
   4:      {
   5:          ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
   6:      }
   7:      for (int i = 0; i < this._size; i++)
   8:      {
   9:          if (match(this._items[i]))
  10:          {
  11:              return this._items[i];
  12:          }
  13:      }
  14:      return default(T);
  15:  }




Vemos que el truco está en la línea 9, ahí llama al predicado y le pregunta si el item actual (dentro de la iteración) cumple con alguna condición que estará en el predicado. Vamos a ver una aplicación práctica

Ejemplo 2

Tenemos la siguiente entidad


   1:  class Persona
   2:  {
   3:      public int Edad;
   4:      public string Nombre;
   5:      public string Apellido;
   6:   
   7:      public Persona(string nombre, string apellido, int edad)
   8:      {
   9:          this.Edad = edad;
  10:          this.Apellido = apellido;
  11:          this.Nombre = nombre;
  12:      }
  13:   
  14:      public override string ToString()
  15:      {
  16:          return "Nombre=" + this.Nombre +" Apellido=" + this.Apellido +", Edad=" + this.Edad.ToString();
  17:      }
  18:  }

Ahora, creamos una lista de Persona y tratamos de recuperar, digamos, a mi.
 
   1:  List lista = new List(); 
   2:   
   3:  lista.Add(new Persona("Leonardo","Micheloni",31));
   4:  lista.Add(new Persona("Hernán", "Micheloni", 30));
   5:  lista.Add(new Persona("Adrián", "Micheloni", 34));
   6:   
   7:  Persona leonardo = lista.Find(new Predicate(delegate(Persona persona)
   8:  {
   9:      return persona.Nombre.Equals("Leonardo");
  10:  }));
  11:   
  12:  Console.WriteLine(leonardo);

Observemos este código, hasta la línea 6 se encuentra la creación de la lista de Persona, ahora en la línea 7 está el yeite. Primero invocamos al método Find de la lista genérica el cual espera un predicado, entonces lo declaramos como un método anónimo, simplemente le decimos que sea true si el nombre es igual a Leonardo, y listo, es todo.
Existe otro predicado predefinido en el framework que aparece en la lista genéricas, es el Action y se utiliza para aplicar una acción a los elementos de la colección.

Conclusión

Los delegados son elementos muy poderosos que nos permiten un grado de flexibilidad muy grande, recuerden el ejemplo de la lista, y todos los que se les ocurran; la utilidad de los métodos anónimos es grande también pero creo que no es bueno abusar porque queda un código algo confuso. Existe otra utilidad para los delegados que son los eventos pero eso será tema de otro post, hasta pronto.