lunes, 11 de agosto de 2008

Utilizando expresiones Lambda para facilitar la creación de métodos anónimos

Utilizando las expresiones lambda para facilitar algunos trabajos molestos

Más de una vez nos encontramos con la necesidad de ordenar una lista tipada (o sea una lista generica) y es necesario implementar IComparer<T>, no es que sea para mucho, es sólo un método como vemos en su definición

 

using System;

namespace System.Collections.Generic
{
    public interface IComparer<T>
    {
        int Compare(T x, T y);
    }
}

pero más de una vez no tenemos ganas de crear una clase para esto o implementarla en una existente, bueno, para evitar hacer esto podemos utilizar un delegado y valernos de la inferencia de tipos de C# del siguiente modo

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication10
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program().Prueba();
            Console.ReadLine();
        }

        private void Prueba()
        {
            List<Persona> personas = new List<Persona>()
            {
                new Persona() { Nombre = "Hernán", Apellido = "Crespo" },
                new Persona() { Nombre = "Leonardo", Apellido = "Micheloni" },
                new Persona() { Nombre = "Enzo", Apellido = "Francescoli" },
                new Persona() { Nombre = "Ariel", Apellido = "Ortega" }
            };

            personas.Sort(delegate(Persona x, Persona y)
            {
                return x.Apellido.CompareTo(y.Apellido);
            });

            personas.ForEach(Console.WriteLine);         
        }
    }

    public class Persona
    {
        private string _nombre;

        public string Nombre
        {
            get { return _nombre; }
            set { _nombre = value; }
        }
        private string _apellido;

        public string Apellido
        {
            get { return _apellido; }
            set { _apellido = value; }
        }

        public override string ToString()
        {
            return string.Format("{0} {1}", this.Apellido, this.Nombre);
        }
    }
}

Y esto funciona muy bien, simplemente creamos un delegado que represente al método Compare<Persona> y la inferencia de tipos hacer el resto, lee el método anónimo se fija los parámetros, el valor de retorno, en cuál de las sobrecargas del método Sort encaja y listo, mágico y muy bonito, pero vamos a ir un paso más allá.

Facilitando la creación de métodos anónimos con expresiones Lambda

Por definición las expresiones Lambda son funciones anónimas que pueden contener expresiones o declaraciones y pueden ser utilizadas para crear delegados o árboles de expresión, entonces podemos reemplazar el delegado con una bonita expresión Lambda, sí, de este modo:

 

private void Prueba()
{
    List<Persona> personas = new List<Persona>()
    {
        new Persona() { Nombre = "Hernán", Apellido = "Crespo" },
        new Persona() { Nombre = "Leonardo", Apellido = "Micheloni" },
        new Persona() { Nombre = "Enzo", Apellido = "Francescoli" },
        new Persona() { Nombre = "Ariel", Apellido = "Ortega" }
    };

    personas.Sort((x, y) => x.Apellido.CompareTo(y.Apellido));

    personas.ForEach(Console.WriteLine);         
}

 

Hermoso, con una linda línea podemos ordenar el listado de ídolos de River Plate (en el cual me incluyu :-P ) y si descompilamos con Reflector vemos lo que pasó

 

internal class Program
{
    // Methods
    private static void Main(string[] args)
    {
        new Program().Prueba();
        Console.ReadLine();
    }

    private void Prueba()
    {
        List<Persona> <>g__initLocal0 = new List<Persona>();
        Persona <>g__initLocal1 = new Persona();
        <>g__initLocal1.Nombre = "Hern\x00e1n";
        <>g__initLocal1.Apellido = "Crespo";
        <>g__initLocal0.Add(<>g__initLocal1);
        Persona <>g__initLocal2 = new Persona();
        <>g__initLocal2.Nombre = "Leonardo";
        <>g__initLocal2.Apellido = "Micheloni";
        <>g__initLocal0.Add(<>g__initLocal2);
        Persona <>g__initLocal3 = new Persona();
        <>g__initLocal3.Nombre = "Enzo";
        <>g__initLocal3.Apellido = "Francescoli";
        <>g__initLocal0.Add(<>g__initLocal3);
        Persona <>g__initLocal4 = new Persona();
        <>g__initLocal4.Nombre = "Ariel";
        <>g__initLocal4.Apellido = "Ortega";
        <>g__initLocal0.Add(<>g__initLocal4);
        List<Persona> personas = <>g__initLocal0;
        personas.Sort(delegate (Persona x, Persona y) {
            return x.Apellido.CompareTo(y.Apellido);
        });
        personas.ForEach(new Action<Persona>(Console.WriteLine));
    }
}
En las líneas remarcadas vemos que efectivamente el compilador creó el mismo delegado que habíamos definido en el primer ejemplo, sólo que nuestro código quedó mucho mas bonito y compacto gracias a las expresiones Lambda.
Hasta la próxima.