domingo, 16 de agosto de 2015

Novedades C# 6.0 (Parte 2)




Esta segunda entrega de novedades de C# 6.0, la vamos a utilizar por completo para explicar el Null Conditional Operator (.?) o Evaluador de null en código. Parte de este operador, ya era un viejo conocido, utilizado en versiones anteriores, para realizar operaciones ternarias y en conjunción doble para tratamientos de valores nulos en tipos por referencia y en tipos nullables. En esta nueva versión, toma de nuevo protagonismo al vincularse con el punto (.) y nos intenta hacer la vida un poquito más fácil.


Vamos a ver de qué va.








Entregas anteriores de novedades C# 6.0

Parte 2



Para estos ejemplos vamos a ayudarnos de una versión de nuestra clase favorita de pruebas, que no es otra que la clase Persona, en esta ocasión, la utilizaremos en conjunción de la clase Direccion, que formará parte de su composición:


public class Persona
{
        public string    DNI        { get; set; }
        public string    Nombre       { get; set; }
        public int       Edad        { get; set; }
        public DateTime? FechaEntrada { get; set; }
        public Direccion Direccion    { get; set; }
}

public class Direccion
{
        public string Calle     { get; set; }
        public int?   Numero    { get; set; }
        public string Localidad { get; set; }
}




Comparaciones con NULL


Normalmente, dentro de nuestros métodos, deberíamos de comprobar para cada uno de los parámetros de tipos por referencia, si estos, han sido instanciados, vamos si no tienen el valor null, porque de realizar esta comprobación, se producirá nuestra excepción favorita:



























Pongamos un ejemplo de un método en el que controlamos la instanciación de nuestros parámetros:


public static bool EsDeValdenalga(Persona persona)
{
    var resultado = false;

    var localidadABuscar = "Valdenalga";

    if( persona                     != null &&
        persona.Direccion           != null &&
        persona.Direccion.Localidad == localidadABuscar)
    {
        resultado = true;
    }

    return resultado;
}


Este método simplemente comprueba que una persona sea de ese maravilloso paraje imaginario llamado ‘Valdenalga’. Para ello tenemos que comprobar que la referencia principal del parámetro de tipo persona, no sea nula. No nos podemos conformar con eso, porque su propiedad Dirección, también es de tipo referencia, por lo que deberemos comprobar que esté igualmente instanciada. Una vez seguros ya podemos acceder a Localidad.

Llega el momento de emplear la novedad a la que dedicamos este post, como veremos reduciremos el tamaño del código y aumentaremos la compresión del mismo. Lo iremos haciendo por fases, para comprender mejor el funcionamiento.


Primero eliminaremos la comprobación de null, para la propiedad de tipo Dirección, y haremos uso de nuestro operador en la verificación de localidadABuscar, quedando así:


public static bool EsDeValdenalga(Persona persona)
{
    var resultado = false;

    var localidadABuscar = "Valdenalga";

    if( persona                     != null &&
        persona.Direccion?.Localidad == localidadABuscar)
    {
        resultado = true;
    }

    return resultado;
}


Esta es la línea clave:


persona.Direccion?.Localidad == localidadABuscar


Cuando la ejecución llega a esta línea, lo que hace el compilador es comprobar si el objeto que está con anterioridad al símbolo ? es null. En caso de serlo, simplemente se olvida de todo lo demás y devuelve un null. En caso negativo continúa con la ejecución, como en nuestra línea, accediendo a la propiedad Localidad y realizando la comprobación con localidadABuscar.


Bueno y si hemos hecho esto con la última comprobación del if, por qué no hacer lo mismo con la primera ¿?.  Pues claro:


public static bool EsDeValdenalgaNuevo(Persona persona)
{
    var resultado = false;

    var localidadABuscar = "Valdenalga";

    if (persona?.Direccion?.Localidad == localidadABuscar) resultado = true;

    return resultado;

}


Pues este es el método de desenlace, así pinta de bien.




Accediendo a métodos y a propiedades

Con el uso de este operador, podemos hacer llamadas a métodos y acceder a propiedades de objetos sin instanciar.


Vamos a actualizar nuestra clase persona, con unos cuantos métodos:


public class Persona
{
    public string    DNI   { get; set; }
    public string    Nombre  { get; set; }
    public int       Edad   { get; set; }
    public DateTime? FechaEntrada { get; set; }
    public Direccion Direccion  { get; set; }

    public void CumplirAños(int añosACumplir = 1) => Edad += añosACumplir;

    public string DecirMiNombre() => $"Mi nombre es {Nombre}";

    public int CuantosAñosParaJubilacion() => 65 - Edad;

}


Si alguno ve rara la definición de los métodos, que se repase nuestra primera entrega de novedades de C# 6.0.


En esta nueva versión, podremos hacer cosas como estas:


[TestMethod]
public void Accediendo_A_Metodo_De_ObjetoNull_Sin_Excepcion()
{
    Persona p = null;

    p?.CumplirAños();
}


Como explicamos en el punto anterior, el compilador, se encuentra con el símbolo ‘?’ y comprueba si el objeto a su izda es nulo, para realizar o no la acción. Por lo que en este caso no ejecutaría nada, y no lanzaría excepción.

Si nos ponemos serios, esto no tiene ninguna funcionalidad, pero solo lo he plasmado aquí para que lo probéis y veáis que no paraliza la ejecución.  Debemos de tener cuidado al hacer uso de código de este tipo, porque no tendremos ninguna constancia de si esto se ha ejecutado o no.


De idéntica manera se comportará el acceso a las propiedades del objeto, dándonos como resultado un null cada vez que accedamos a una propiedad de un objeto sin instanciar:


[TestMethod]
public void Accediendo_A_Propiedades_De_Objetos_Null()
{
    Persona p = null;

    var dni = p?.DNI;

    Assert.IsNull(dni);
}


Puede parecer que no es nada útil, pero veremos que en momentos nos puede ahorrar escribir ese código en ocasiones tan tedioso que es el de las comprobaciones.





En conjunción con el operador ??

Voy a hacer un pequeño resumen de este operador, por si alguien lo desconoce, para que pueda entender mejor este punto, en caso de conocerlo, sáltatelo sin problema.


Este operador básicamente es consumido en las asignaciones y no puede ser utilizado con los tipos por valor (struct), solo puede ser empleado por los tipos por referencia y por los tipos nullables, siendo estos últimos con los que más se usa.


                     // Nullable<DateTime>
public static bool EsAñoBPar(DateTime? fecha = null)
{
    bool resultado = false;

    /// Si fecha tiene valor, se le asígna a fechaDefinitiva
    /// Si fecha == null, se le asigna a fechaDefinitiva el valor
    /// de resultado de después del oerador '??'
    DateTime fechaDefinitiva = fecha ?? DateTime.Today;

    resultado = fechaDefinitiva.Year % 2 == 0;

    return resultado;
}


Creo que los comentarios lo dejan bastante claro, pero si alguien sigue teniendo algún tipo de duda, dejo aquí el enlace a la MSDN.




Volviendo a lo que nos importa para este post, vamos a ver la mezcla de estos dos operadores y como esto sí que nos va a hacer ver un poquito más de color y de utilidad.

Como hemos aclarado ya unas cuantas veces en lo que llevamos de post, cuando accedemos a cualquiera de los miembros de un objeto sin instanciar mediante ‘?.’, este devuelve el valor null. Esto ocurre indistintamente tanto si lo hacemos a Métodos, Propiedades, etc. Vamos a demostrarlo:















Como vemos en la imagen, estamos intentando asignar el resultado de la propiedad Edad, que es de tipo int, a una variable edad igualmente de tipo int, y el compilador no nos deja elaborar la acción cuando utilizamos el operador ‘?.’. Esto se debe a que sabe que por mucho que la propiedad o la firma del método den como resultado un tipo no nullable, a usar este operador, el desenlace puede ser null,  por lo que no tiene cabida dentro de un objeto System.Int32. Por eso nos dice que no puede realizar la conversión a un int? (Nullable<int>) de manera explícita.

Para no tener que inicializarlo en variables nullables y luego tener que preguntar si tienen valor y … … … (rolletes), unificaremos su uso con el operador ?? y nos ahorramos trabajo, dejando nuestro código mucho más bonito. Así quedaría el ejemplo anterior:


static void Main(string[] args)
{
    Persona p = null;

    int edad = p?.Edad ?? 0;

    int añosParaJubilarse = p?.CuantosAñosParaJubilacion() ?? 65;
}


En definitiva mucho más legible y mucho más de estos tiempos que corren.


Para que todo no sean tipos nullables, voy a poner un ejemplo con una clase, y dentro de un método. Esto cobra mucho más sentido, porque nadie en su sano juicio (debería) va a instanciar una propiedad a null y se va a poner a utilizarla. Cuando el valor de la clase viene por parámetros, no sabemos nunca con veracidad que este haya sido instanciado. 


public static string PersonaDesc(Persona persona)
{
    string dni    = persona?.DNI               ?? "'Sin DNI'";
    string nombre = persona?.Nombre            ?? "'Sin Nombre'";
    int    edad   = persona?.Edad              ?? 0;
    string direc  = persona?.Direccion?.Calle  ?? "'No se ha comprado piso'";

    string resultado = $"{dni} - {nombre} - {edad} año(s) - {direc}";

    return resultado;
}



LINQ

Esto también tiene su introducción en el mundo de LinQ, por ejemplo para saber si una colección tiene elementos:

public static void HacerConPersonas(List<Persona> personas)
{
    if((personas?.Count ?? 0) > 0)
    {
        /// hacer Cosas
    }

    if (personas?.Any() ?? false)
    {
        /// Hacer Cosas
    }
}


O mediante la utilización de cualquier operador _OrDefault (FirstOrDefault, SingleOrDefault, etc):

public static void DimeElNombreDelDNI(List<Persona> personas, string dni)
{
    var persona = personas.FirstOrDefault(p => p.DNI == dni);

    string nombre = persona?.Nombre ?? "No se ha encontrado nombre";

    Console.WriteLine($"El nombre del DNI {dni} es {nombre}");
}




En uso para eventos

Otra de las comodidades que nos ofrece el operador ?., es el uso con los manejadores de eventos. Como todos sabemos, cuando implementamos un evento dentro de nuestra clase, siempre antes de lanzarlo en nuestro código, debemos de preguntar si alguien se ha suscrito a él, ósea tenemos preguntar si el valor de su delegado es null. Con el operador .? nos libraremos de este lastre.

Vamos a añadir a nuestra clase Persona, un evento que se lance cuando una persona tenga más de 18 años. Ya hicimos algo parecido en nuestra entrada de Eventos del curso de LinQ, lo podemos repasar aquí . La clase quedaría así:


using System;

namespace NovedadesCSharp6_2
{
    public class Persona
    {
        public  string DNI    { get; set; }
        public  string Nombre { get; set; }
        private int _edad;

        public int Edad
        {
            get { return _edad; }
            set
            {
                _edad = value;

                if (Edad >= 18) OnMayorDeEdad();
            }
        }

        public DateTime? FechaEntrada { get; set; }
        public Direccion Direccion    { get; set; }

        public void CumplirAños(int añosACumplir = 1) => Edad += añosACumplir;

        public string DecirMiNombre() => $"Mi nombre es {Nombre}";

        public int CuantosAñosParaJubilacion() => 65 - Edad;


        public event EventHandler MayorDeEdad;

        protected virtual void OnMayorDeEdad()
        {
            if (MayorDeEdad != null) MayorDeEdad(this, new EventArgs());
        }
    }
}

Para ello hemos añadido un evento de tipo EventHandler (evento simple del Framework, que realiza una acción y solo informa del objeto que realiza la acción). Y con él un método On(NombreEvento), que será posible redescribir por sus clases derivadas (esto lo deberían de tener todos los eventos). En este método haremos la comprobación de suscripción, para no tener que realziarla en cada llamada al evento. Después en el set de la propiedad Edad, comprobaremos si tiene más de 18 años para lanzar nuestro evento MayorDeEdad, mejor dicho nuestro método OnMayorEdad, que es el que encapsula la llamada al evento.

Nosotros nos centraremos en el método OnMayorDeEdad, y en su comprobación de null. Como llevamos viendo en todo este post, con el operador ?. en caso de que el objeto o la propiedad sea null, esta no se accederá/lanzará y por ende devolverá un null. Con los eventos es algo parecido, pero con una diferencia. Esta diferencia radica en que el posible valor null, no es el del objeto ‘padre’, es el del evento perteneciente al objeto padre.


Para poder tomar ventaja de esto, no llamaremos directamente al delegado del evento como en el código anterior, utilizaremos otra fórmula equivalente, que nos permita utilizar el operador. La fórmula es emplear el método Invoke del delegado subyacente al evento, que es igual de válido, y que nos permite introducir el operador ?. entre el evento (que es el objeto que puede estar en null) y el método (Invoke) a llamar para ejecutar. Aquí tenéis más información sobre los delegados y sus formas de ejecución, tanto síncronas como asíncronas.


protected virtual void OnMayorDeEdad()
{
    MayorDeEdad?.Invoke(this, new EventArgs());
}


Si alguien se hubiera suscrito se lanzará el evento, y si no, no se haría nada, sin necesidad de tener que indicar el if.


Como no me gusta que se quede nada en el aire, pondré el ejemplo original (con if) utilizando Invoke, en la llamada del evento:


protected virtual void OnMayorDeEdad()
{
    if (MayorDeEdad != null) MayorDeEdad.Invoke(this, new EventArgs());
}


Igual de funcional.






Pues hasta aquí este post, con Null Conditional Operator.  Tenía en mente haber puesto algún ejemplo de WPF, para cuando utilizamos CommandBindings, y estos realizan comprobaciones (CanExecute) dentro de la ventana antes de que se hayan creado los controles, en este caso nuestro operador de hoy también nos ayuda y mucho. El método Find() de los DbSet de EntityFramework 4.2 y posterior, también se beneficia de ello, pero creo que ya ha sido el post lo bastante largo, como para hacerlo un poquito más pesado.


En el siguiente seguiremos con más novedades de nuestro C# 6.0.







No hay comentarios :

Publicar un comentario