viernes, 17 de julio de 2009

Generics en .Net

Muchos han escuchado el termino de clase genérica o un tipo genérico, y esto es lo que vamos a definir en esta entrada. 

¿Qué es algo genérico en la vida cotidiana?, puede ser un producto que no tenga una marca como tal, pero igual funciona. En programación podemos utilizar el mismo concepto pero para nuestro código, en donde el tipo se especifica en tiempo de ejecución. Éste tipo de técnicas nos van a permitir reducir la duplicación de código y también el uso de interfaces o clases base en momentos que no son necesarios.

Esto quiere decir que con Generics podemos reducir significativamente la cantidad de código en operaciones que sean repetitivas a lo largo de la aplicación, pero que se efectúen con varios tipos que no tienen nada que ver unos con otros.

Para los programadores de C++ es un concepto parecido a los templates.

Existen varios lenguajes que soportan Generics, entre los cuales están: Java, Eiffel, C++(templates), Ada, Haskell,C#,VB.Net, entre otros.

Ahora vamos a poner en practica los conceptos para poder entenderlo un poco mejor:

public class Program

{

     public static void Main(string[] args)

     {

          int resultInt = GetFibonacci<int>(30);

          long resultLong = GetFibonacci<int>(45000);

     }

     public static T GetFibonacci<int>(T number)
     {
          if (x <= 1)

               return 1;

          return GetFibonacci<T>(x-1) + GetFibonacci<T>(x-2);    

      } 

}

Como podemos observar creamos un método en donde el tipo del parámetro y el retorno es del mismo tipo que se le asigna en el momento de llamarlo colocando el tipo entre los simbolos < y >.

Ahora que pasa si en T colocamos Person o algo parecido, vamos a tener un problema, y la única forma de resolverlo es utilizando condiciones en la asignación del tipo. Es decir, podemos restringir el uso haciendo que T implemente alguna Interfaz o Herede de alguna clase, esto se hace mediante la palabra reservada where.

Ahora necesitamos un método que obtenga el valor menor de un arreglo:

public static T GetMinimun<T>(IList list) where T : IComparable<T>

{

     T result = list[0];

     for(int i =0; i < list.Count ; i++)

     {

          if(list[i] < result)

               result = list[i];

     }

     return result;

}

Podemos ver que utilizamos "where" para restringir el paso de cualquier tipo y solamente permitir a aquellos que implementen IComparable. Aunque funciona perfectamente el ejemplo sin el where, puede darse el caso de que se utilice con algún tipo que no tenga implementada la interfaz por lo que nos daría un error al intentar comparar con otro objeto del arreglo.


Como punto final veremos el uso de delegados genéricos para así completar el post de Delegados.

Existe la posiblidad de crear clases genericas y métodos genéricos, pero también podemos definir delegados genéricos, los cuales nos van a ayudar a reutilizarlos y solamente intercambiar la clase de los argumentos, ejem:

public delegate void MyDelegate<T>(object sender, T e);

Esto nos permite poder crear eventos sin estar codificando y codificando un delegado para cada uno. Ejmplo de uso:

public class TestClass

{

      public event MyDelegate<MouseEventArgs>(object sender, MouseEventArgs e);

      public event MyDelegate<MyOwnEventArgs>(object sender, MyOwnEventArgs e);

}

De esta forma nos ahorramos bastantes líneas de código, disminuyendo el tiempo de desarrollo de la aplicación.


Gracias por leer!

1 comentario:

  1. Exacto!

    Solo para complementar, tambien es posible indicar un where para decir que el tipo implemente o herede de algun otro tipo base obligatoriamente como por ejemplo.

    public void DoSomethingToControl(T control) where T : Control
    {
    ...
    }


    En ese método solo se puede pasar como argumento de tipo una clase que herede de control como TextBox o Button, de este modo el compilador está realmente seguro de que hacer cosa que se nota en intellisense al momento de utilizar el argumento 'control'

    Ej.
    DoSomethingToControl<Button>(myButton); // funciona.

    DoSomethingToControl<Customer>(myCustomer) // No funciona.

    Saludos!

    ResponderEliminar