.Net Core 3.0: Oficialmente Lanzado

El 23 de septiembre se anuncio el lanzamiento oficial de .Net Core 3.0. Esta versión tiene muchas mejoras muy esperadas por los desarrolladores. Entre ellas se encuentran la posibilidad de crear aplicaciones Windows Forms y Windows Presentation Foundation con .Net Core, una nueva API JSon, nuevos soportes para ARM64 y muchas mejoras de rendimientos. También se encuentra incluida la versión 8 de C# con características muy interesantes que veremos en este post.

Para empezar veamos los cambio en los componentes. Para esta comparación he utilizado la herramienta NDepend la cual nos permite comparar las 2 versiones de .Net Core. En nuestro caso la versión 2.2 y la 3.0. Puedes descargar la herramienta desde aquí.
Una vez instalada la aplicación y ejecutada, seleccionaremos la opción Compare 2 versions of a code base.

Luego seleccionaremos las carpetas a comparar y le presionaremos el boton ok.

A simple vista podemos ver que disponemos de mas cantidad de ensamblados, 156 para la versión 2.2 y 165 para la versión 3.0. Una vez que términos la ejecución de las comparaciones, mediante algunas consultas, podemos obtener los siguientes resultados:

  • 29 espacios de nombres nuevos.
  • 359 tipos nuevos.
  • 3845 miembros nuevos.
  • 1044 métodos nuevos.
  • 166 Campos nuevos.

Con estos números podemos ver que el equipo de .Net Core ha trabajado muy arduamente y el framework ha crecido significativamente en funcionalidades.

Les dejo los links a las consultas realizadas y los reportes que nos entrega la herramienta NDepend. Aquí.

Novedades

Podemos descargar .Net Core 3.0 desde aqui. También se encuentran disponibles la nueva versión de Visual Studio 2019 16.3 y Visual Studio para MAc 8.3 con el soporte necesario para utilizar .Net Core 3.0. Otros lanzamientos relacionado, tambien muy importantes, son ASP.Net Core 3.0 y Entity Framework 3.0.

Plataformas Soportadas

.Net Core 3.0 es compatible con los siguientes sistemas operativos:

  • Alpino: 3.9+
  • Debian: 9+
  • openSUSE: 42.3+
  • Fedora: 26+
  • Ubuntu: 16.04+
  • RHEL: 6+
  • SLES: 12+
  • macOS: 10.13+
  • Cliente de Windows: 7, 8.1, 10 (1607+)
  • Servidor Windows: 2012 R2 SP1 +

Debemos tener en cuenta que Windows Forms y Windows Presentation foundation por el momento solamente funcionan en entornos Windows.

El soporte para Chips es:

  • x64 en Windows, macOS y Linux
  • x86 en Windows
  • ARM32 en Windows y Linux
  • ARM64 en Linux (kernel 4.14+)

Nota: .NET Core 3.0 ARM64 necesita la versión 4.14 del kernel de Linux o superior. 

Puntos más destacados

Las mejoras clave más importantes que  llaman la atención son las siguientes:

  • .NET Core 3.0 ha sido probado durante meses por muchos equipos de Microsoft que pronto implementarán en entornos productivos con grandes cargas de trabajo .
  • El rendimiento se ha mejorado notoriamente en varios sus componentes.
  • C # 8 agrega streams asíncronos, rango e índices, más patrones y tipos de referencia anulables. Nullable nos permite apuntar directamente a fallas en el código que conducen a NullReferenceException . Puede verlo en mi post anterior donde trato estas mejoras. aquí.
  • .NET Standard 2.1 incrementa el conjunto de tipos que podemos usar en el código. Puede usarse tanto con .NET Core como con Xamarin. .
  • Las aplicaciones de escritorio de Windows ahora son compatibles con .NET Core, tanto para Windows Forms como para WPF (y de código abierto). El diseñador de WPF es parte de Visual Studio 2019 16.3. El diseñador de Windows Forms todavía está en versión preliminar y disponible como descarga VSIX. Descargalo desde aqui
  • Las aplicaciones .NET Core ahora tienen ejecutables por defecto. En las versiones anteriores, las aplicaciones debían iniciarse con el comando dotnet myapp.dll , como dotnet myapp.dll. Nuestras aplicaciones ahora pueden iniciarse con un ejecutable específico de la aplicación, como myapp o ./myapp , dependiendo del sistema operativo.
  • Se han agregado API JSON de alto rendimiento para lectura y escritura, modelos de objetos y escenarios de serialización nuevos. Estas API fueron creadas desde cero sobre el genérico Span<T> y utilizan UTF8 en lugar de UTF16 . Estas API minimizan las asignaciones, lo que da como resultado un rendimiento superior y mucho menos carga de trabajo para el recolector de basura. 
  • El recolector de basura utiliza menos memoria de forma predeterminada. Esta mejora es beneficiosa para escenarios en los que varias aplicaciones están alojadas en el mismo servidor. El recolector de basura también se ha actualizado para hacer un mejor uso de gran cantidad de núcleos, en máquinas con más de 64 núcleos.
  • .NET Core ha sido mejorado para la utilización con Docker permitiendo que las aplicaciones .NET funcionen de manera predecible y eficiente en contenedores. El recolector de basura y el grupo de subprocesos se han actualizado para que funcionen mucho mejor cuando se ha configurado un contenedor con memoria limitada o CPU. Las imágenes Docker de .NET Core son más pequeñas, particularmente la imagen que contiene el SDK.
  • Los Raspberry Pi y ARM ahora son compatibles para permitir el desarrollo de IoT, incluso podemos utilizar el depurador remoto de Visual Studio. Podemos implementar aplicaciones que escuchen sensores e impriman mensajes o imágenes en una pantalla, todo utilizando las nuevas API GPIO. ASP.NET se puede usar para exponer datos como una API o como un sitio que permite configurar un dispositivo IoT.
  • .NET Core 3.0 será reemplazada por .NET Core 3.1 , a mediados de noviembre de este año .NET Core 3.1 será una versión compatible a largo plazo (LTS) (compatible durante al menos 3 años). Microsoft recomienda que se adopte antes .NET Core 3.0 y luego adoptar 3.1 para que nos sea más fácil actualizar nuestras aplicaciones.
  • .NET Core 3.0 estará disponible con RHEL 8 en Red Hat Application Streams, después de varios años de colaboración con Red Hat.
  • Visual Studio 2019 16.3 y Visual Studio para Mac 8.3 es es necesarios para el soporte .NET Core 3.0.
  • Los usuarios de Visual Studio Code debemos usar siempre la última versión de la extensión C # para asegurarse de que funcionen los escenarios más recientes.
  • La implementación de Azure App Service de .NET Core 3.0 está actualmente en curso.
  • La implementación de Azure Dev Ops de .NET Core 3.0 se realizará próximamente. Se actualizará cuando esté disponible.

Conclusión

.NET Core 3.0 es una nueva versión que  incluye un amplio conjunto de mejoras que nos tendrán entretenidos un buen tiempo. Es muy recomendable comenzar adaptar nuestras aplicaciones a la nueva versión para mejorar nuestro rendimiento y soporte. No debemos olvidar que dentro de unos meses estará disponible la versión 3.1, así que es una buena opción migrar nuestras app.

En próximos post veremos más en detalle las nuevas características y funcionalidades.


[Article] Novedades de C# 8

Hoy vamos a ver algunas de las nuevas funcionalidad más interesantes que vendrán en C# 8. Para esto ver algunos ejemplos usaremos Visual Studio 2019 que se encuentra en su versión preview hasta el momento. También, deberemos instalar el SDK de .Net Core 3.0. Les dejo los links.

https://visualstudio.microsoft.com/es/vs/preview/

https://dotnet.microsoft.com/download/dotnet-core/3.0

Nullable reference types

Lo primero que vamos a ver son los  tipos nulos. Todos sabemos lo costoso que es cuando tenemos muchas excepciones que nos devuelve NulLReferenceException, más que todo, en un ambiente de producción.

Supongamos en siguiente caso: Tenemos un servicio que nos devuelve una lista de Personas.  Luego tenemos una metodo GetName, que nos devuelve e libro y el autor concatenados.

static string GetName(Person p)
    {
        return (p.MiddleName != null)
            ? $"{ p.FirstName } { p.MiddleName[0] }. { p.LastName })"
            : $"{ p.FirstName } { p.LastName })";
    }
}

public class Person
{
    public string FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string LastName { get; set; }

    public Person(string first, string last)
    {
        FirstName = first;
        LastName = last;
    }
}

Si esta lista de personas nos devuelve una de sus propiedades, la segunda propiedad se encuentra en null, GetName(), fallará, debido a que se encuentra en null y devolverá la excepción System.NullRerefenceException.

Imaginemos si esto pasa todo el tiempo. Tratar de rastrear este tipo de excepciones a veces es difícil debido a que debemos suponer donde podría ocurrir. Aquí es donde esta nueva característica aparece para ayudarnos.

Lo primero que debemos hacer es activar esta característica de siguiente manera:

Una vez que se encuentra activa, veremos como el Visual Studio comienza a detectar los posibles null. En la siguiente imagen vemos como un campo, que debería ser parte del objeto, no está siendo declarado con Non-nullable.

Una vez que agregamos la declaración del parámetro, le asignamos el valor null, nos avisara que la variable declarada asociada no es nuleable como lo vemos en el siguiente código.

Por último, debemos arreglar nuestro método. Nos indica que es posible que uno de las propiedades posiblemente sea null en un momento en particular.

Entonces para solucionar esto podemos usar las siguientes líneas de código.

Esta funcionalidad no ayudará a prevenir muy eficientemente las situaciones donde es posible, que un error en el código, nos devuelva System.NullRerefenceException.

Async streams

Esta funcionalidad nos permite consumir o producir resultado asíncronos sencillamente sin la necesidad tener que devolver algo desde las llamadas. Veamos el siguiente código:

async Task GetBigResultAsync() { 
var result = await GetResultAsync(); if (result > 20) return result; else return -1; 
}

Supongamos que tenemos una lista, si estamos suscritos a un servicio o por ejemplo algún componente IoT no se reproducen tan fácilmente. Aquí es donde entra en juego la nueva Interfaz IAsyncEnumerable que es la versión asíncrona de IEnumerable. Esto nos permitirá que podamos agregar la palabra clave await a un foreach permitiendo recorrer los elementos a medida que van llegando

Ranges and indices

Ranges and indices es una funcionalidad que me gusta mucho. Supongamos nuevamente que tenemos un array o una collection, pero no deseamos recorrer desde el inicio hasta el final, sino solamente una parte. Actualmente deberíamos recorrer nuestro array omitiendo ciertas posición o bien hacer una subconsulta en linq devolviendo un nuevo resultado con el contenido deseado.

Para evitar esto podemos utilizar rango o índices. Veamos el siguiente código, vamos a decirle que solamente quiero recorrer desde 1 hasta 3.

foreach(var p in people[1..3]) yield return p;

Default implementations of interface members

Esta interesante característica nos ayudará a que la interfaces evolucionen más fácilmente. Supongamos que tenemos una interface que ya fue implementada. Si modificamos esa interfaz, seguramente, las implementaciones fallaran..

Para solucionar este inconveniente al modificar la interface podemos implementar, en la interface, un comportamiento por defecto:

interface ILogger
{
     void Log(LogLevel level, string message);
     void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload
}
class ConsoleLogger : ILogger
{
     public void Log(LogLevel level, string message) { … }
     // Log(Exception) gets default implementation
}

La clase ConsoleLogger no tiene implementado la sobrecarga del método Log, pero, al estar declarada en la interface como una implementación determinada no romperemos nuestras implementaciones antiguas.

Recursive patterns

C# 8 ahora nos permite tener patrones que contengan otros patrones. Por ejemplo:

IEnumerable GetEnrollees()
 {
     foreach (var p in People)
     {
         if (p is Student { Graduated: false, Name: string name }) yield return name;
     }
 }

En este código estamos verificando que p sea del tipo que se le asigna, si lo es, le asigna un valor de false a la propiedad y devolverá el name como nulo.

Switch expressions

¿Quién no ha sufrido la necesidad de en un switch validar todas combinaciones posibles? Como también, evaluar en ciertos casos un valor de un objeto. Para verlo en más detalle lo que estamos hablando les mostrare un código clásico:

  switch (p.FirstName, p.MiddleName, p.LastName)
            {
                case (string f, string m, string l):
                    return $"{ f } { m[0] }. { l }";
                case (string f, null, string l):
                    return $"{ f } { l }";
                case (string f, string m, null):
                    return $"{ f } { m }";
                case (string f, null, null):
                    return f ;
                case (null, string m, string l):
                    return $"Ms/Mr { m[0] }. { l }";
                case (null, null, string l):
                    return $"Ms/Mr { l }";
                case (null, string m, null):
                    return $"Ms/Mr { m }";
                case (null, null, null):
                    return "Someone";
            }

Verdaremante se hace muy difícil de leer. Ahora podemos, con C# 8, mediante Switch Expressions, hacerlo más legible y sencillo de la siguiente forma:

return (p.FirstName, p.MiddleName, p.LastName) switch
             {
                 (string f, string m, string l) =>  $"{ f } { m[0] }. { l }",
                 (string f, null, string l) =>  $"{ f } { l }",
                 (string f, string m, null) => $"{ f } { m }",
                 (string f, null, null) => f,
                 (null, string m, string l) => $"Ms/Mr { m[0] }. { l }",;
                 (null, null, string l) => $"Ms/Mr { l }",
                 (null, string m, null) =>  $"Ms/Mr { m }",
                 (null, null, null) => "Someone";
             }

Target-typed new-expressions

Se que van amar esta nueva funcionalidad. Anteriormente cuando declaramos una array de algún tipo de objeto y queríamos agregarle objetos nuevos debíamos hacerlo de esta manera:

Person[] people =
                {
                    new Person("Fernando", "A", "Sonego"),
                    new Person("Ricardo", "E", "Gomez"),
                    new Person("Alfredo", "X", "Rodriguez"),
                    new Person("Miguel", "A", "Gimenez")
                };

Ya no es necesario decirle que el objeto que vamos agregar en el array es del mismo tipo. Automáticamente se reconocerá y solamente debemos declararlo de la siguiente forma:

Person[] people =
                {
                    new ("Fernando", "A", "Sonego"),
                    new ("Ricardo", "E", "Gomez"),
                    new ("Alfredo", "X", "Rodriguez"),
                    new ("Miguel", "A", "Gimenez")
                };

Conclusiones

C# 8 nos trae nuevas y muy interesantes características que nos harán la vida un poco más fácil mientras estamos desarrollando. Además de estas habrá muchas más en el lanzamiento oficial.

Por ahora solo nos queda esperar el lanzamiento de Visual Studio 2019 junto ASP.Net Core 3.0. Mientras tanto podemos descargar las versiones preview e ir jugando para adoptarlas apenas sean oficiales.