Delegados en C#

| |
Usar delegados en C#

Para explicarlo voy a usar ejemplos muy difundidos en Internet, así usamos algo estandarizado y con menos posibilidades de cometer errores.

Para entender que hacen o para que sirven veamos en que momentos se puede necesitar usar

"Ordenar" es un método que sirve para ordenar cualquier cosa, siempre que herede de la interfaz iComparable y se implemente el método CompareTo,

public void Ordenar(IComparable[] items)
{    
for(i=0; i<items.length; i++)
    {       
       Icomparable tmp = items[i];
       items[i] = items[j];
       items[j] = tmp;    
    }
}

Ahora la usamos en un ejemplo, Persona es una clase y se define así:

class csharp public class Persona: IComparable
{
   //Estas propiedades se definen en una sola linea
   public string Nombre { get; set; }

   public Date FecNacimiento { get; set; }

   public float Peso { get; set; }

   public Persona(string Nombre, Date FecNacimiento, float Peso)
   {
      this.Nombre = Nombre;
      this.FecNacimiento = FecNacimiento;
      this.Peso = Peso;
   }

   //Metodo parte de IComparable
   public int CompareTo(object obj)
   {
      Persona p = obj as Persona;
      //Compara las personas por Fecha de Nacimiento
      return this.FecNacimiento.CompareTo(p.Fecnacimiento);
   }
}
//clase Fecha
public class Fecha : IComparable
{
   int dia, mes, año;

   public Fecha(int dia, int mes, int año)
   {
      this.dia = dia;
      this.mes = mes;
      this.año = año;
      if (!IsValid) throw new ArgumentException("La fecha no es válida");
   }

   //Verificar si una fecha es valida o no
   bool IsValid
   {
      get{
         //Verifico si la fecha tiene rango valido
         if (año &lt;= 0 || mes &lt;= 0 || mes &gt; 12 || dia &lt;= 0 || dia &gt; 31)
            return false;
         //Verifico si el año es bisiesto que febrero no tenga mas de 28 dias
         if ((año % 400 == 0 && mes == 2 && dia > 28) || (mes == 2 && dia > 29))
            return false;
         // Verifico los meses que no tienen 31 dias
         if ((mes == 4 || mes == 6 || mes == 9 || mes == 11) &amp;&amp; dia == 31)
            return false;
         return true;
      }
   }

   public int dia
   {
      get { return dia; }
   }
   public int mes
   {
      get { return mes; }
   }
   public int mes
   {
      get { return mes; }
   }

   //Metodo para comparar fecha
   public int CompareTo(object x)
   {
      fecha d = x as Date;
      if (d == null)
         throw new ArgumentException();
     

         return (d.Year > año || (d.Year == año && d.Month > mes) || (d.Year == año && d.Month == mes && d.Day > dia)) ? -1 : (d.Year == año && d.Month == mes && d.Day == dia) ? 0 : 1;
   }
}

class Program
{
   //Main es donde se usan las clases  
   static void Main(string[] args)
   {
      //array de personas

       Persona[] personas = new Persona[]
      {
         new Persona("Mario", new fecha(5, 8, 1955), 80),
         new Persona("Carlos", new
fecha(14, 4, 1965), 55),
         new Persona("Juan", new
fecha(12, 6, 1956), 66),
         new Persona("Raul", new
fecha(21, 5, 1966), 77)
      };

      //Acá ordena el array de personas
      Sort(personas);

      foreach (Persona p in personas)
      {
         Console.WriteLine("Nombre: {0}   Fecha: {1}, {2}, {3}, Peso: {4}",
         p.nombre, p.FechNacimiento.dia, p.FecNacimiento.mes, p.FecNacimiento.año, p.Peso);
      }
      Console.ReadKey();
   }

   static void Ordenar(IComparable[] items)
   {
      for (int i = 0; i < items.Length - 1; i++)
         for (int j = i + 1; j < items.Length; j++)
                
         if (items[i].CompareTo(items[j]) > 0){
            IComparable tmp = items[i];
            items[i] = items[j];
            items[j] = tmp;
         }
   }
}
 

¿Que pasa si tuvieramos que tener diferentes criterios de ordenación para el mismo objeto?, por ejemplo si quisiéramos ordenar a las personas por peso.

No vamos a hacer varios métodos que comparen dentro de la clase Persona y sucedería que el método Ordenar no sirve para comparar todo tipo que fuese IComparable, sino que sería static void Ordenar(Persona[] items).

El uso de delegados entra en juego ahora, ya que el código del método Main tiene que ser flexible y permitir escoger el criterio de comparación que se desea utilizar.

Un delegado es un tipo que puede recibir métodos con una firma específica.
Delegados:

public delegate [valor de retorno] Comparison([parametro 1], [parametro 2], ... , [parametro n]);

Nuestro ejemplo:

public delegate int Comparison(object x, object y);

Nos entrega un entero y se le pasan 2 objetos, es decir puede recibir un método que devuelva un entero y recibe 2 parámetros de tipo object.

Una característica particular de los delegados es que los podemos definir en cualquier parte de nuestro código, es decir no tiene que estar dentro de una clase como los métodos.

Ponemos el delegado en nuestro código, en cualquier lugar:

static void Ordenar(Persona[] items, Comparison compare)
{
   for (int i = 0; i < items.Length - 1; i++)
      for (int j = i + 1; j < items.Length; j++)        
     
      if (compare(items[i],items[j]) > 0)
      {
         Persona tmp = items[i];
         items[i] = items[j];
         items[j] = tmp;
      }
}

Luego agregamos los métodos para  así comparar por nombre, por fecha de nacimiento y por peso:


public static int CompareByName(object x, object y)
{
   Persona p1 = x as Persona;
   Persona p2 = y as Persona;
   return StringComparer.OrdinalIgnoreCase.Compare(p1.Name, p2.Name);}

public static int CompararPorPesot(object x, object y){
   Persona p1 = x as Persona;
   Persona p2 = y as Persona;
   return p1.Peso.CompareTo(p2.peso);}

public static int ComparePorNacimiento(object x, object y){
   Persona p1 = x as Persona;
   Persona p2 = y as Persona;
   return p1.FecNacimiento.CompareTo(p2.FecNacimiento);}

Código cliente:

static void Main(string[] args)
{
   Persona[] personas = new Persona[]
   {
         new Persona("Mario", new fecha(5, 8, 1955), 80),
         new Persona("Carlos", new
fecha(14, 4, 1965), 55),
         new Persona("Juan", new
fecha(12, 6, 1956), 66),
         new Persona("Raul", new
fecha(21, 5, 1966), 77)
   };

   Ordenar(personas, Persona.CompararPorNombre);


   foreach (Persona p in personas)
   {
      Console.WriteLine("Nombre: {0}", p.Nombre);
   }

   Console.WriteLine();
   Ordenar(personas, Persona.CompararPorFecNacimiento);
   foreach (Persona p in personas)
   {
      Console.WriteLine("Fecha: {0}, {1}, {2}",p.FecNacimiento.dia, p.FecNacimiento.mes, p.FecNacimiento.año);
   }

   Console.WriteLine();

   Ordenar(personas, Persona.CompararPorPeso);   foreach (Persona p in personas)
   {
      Console.WriteLine("Peso: {0}",p.peso);
   }
   Console.ReadKey();
}


Ordenar alfabéticamente desde la segunda letra
agregamos:

public static int CompareFromSecondLetter(object x, object y){
   Persona p1 = x as Persona;
   Persona p2 = y as Persona;
   return StringComparer.OrdinalIgnoreCase.Compare(p1.nombre.Substring(1), p2.nombre.Substring(1));}


y lo usamos así

Ordenar(personas, Persona.CompareFromSecondLetter);


Bueno espero algo se entienda, intenté explicarlo lo mejor posible, obviamente hace falta un nivel de conocimiento por encima del básico, pero tampoco es tan dificil. Delegados es un tema extendo, en MSDN hay mucha mas documentación y ejemplos de uso. Saludos.

0 comentarios:

Publicar un comentario

Con la tecnología de Blogger.