Novedades de .NET Core 3.0

En este artículo se describen las novedades de .NET Core 3.0. Una de las mejoras más importantes es la compatibilidad con las aplicaciones de Escritorio de Windows (solo Windows). Mediante el componente Escritorio de Windows del SDK de .NET Core 3.0, puede portar sus aplicaciones de Windows Forms y Windows Presentation Foundation (WPF). Para ser más precisos, el componente Escritorio de Windows solo se admite e incluye en Windows. Para obtener más información, vea la sección Escritorio de Windows más adelante en este artículo.

.NET Core 3.0 agrega compatibilidad con C# 8.0. Se recomienda encarecidamente usar Visual Studio 2019, versión 16.3 o una versión posterior, Visual Studio para Mac 8.3 o una versión posterior, o Visual Studio Code con la última extensión de C# .

Descargue .NET Core 3.0 y empiece a trabajar ya en Windows, macOS o Linux.

Para obtener más información acerca de la versión, consulte el anuncio de .NET Core 3.0.

Microsoft considera .NET Core 3.0 RC 1 como listo para producción y es totalmente compatible. Si usa una versión preliminar, debe pasar a la versión RTM para obtener soporte técnico continuo.

Mejoras del lenguaje C# 8.0

C# 8.0 también forma parte de esta versión, que incluye la característica de tipos de referencia que aceptan valores NULL, flujos asincrónicos y más patrones. Para obtener más información sobre las características de C# 8.0, vea Novedades de C# 8.0.

Tutoriales relacionados con las características del lenguaje de C# 8.0:

Se han agregado mejoras del lenguaje para admitir las siguientes características de API que se detallan a continuación:

.NET Standard 2.1

.NET Core 3.0 implementa .NET Standard 2.1. Pero la plantilla predeterminada dotnet new classlib genera un proyecto que sigue destinado a .NET Standard 2.0. Para destinarlo a .NET Standard 2.1, edite el archivo de proyecto y cambie la propiedad TargetFramework a netstandard2.1:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.1</TargetFramework>
  </PropertyGroup>

</Project>

Si usa Visual Studio, necesita Visual Studio 2019, ya que Visual Studio 2017 no admite .NET Standard 2.1 ni .NET Core 3.0.

Compilación e implementación

Archivos ejecutables predeterminados

.NET Core compila ahora archivos ejecutables dependientes del marco de forma predeterminada. Este comportamiento es nuevo en las aplicaciones que usan una versión de .NET Core instalada globalmente. Anteriormente, solo las implementaciones independientes generarían un archivo ejecutable.

Durante dotnet build o dotnet publish, se crea un archivo ejecutable (conocido como appHost) que coincide con el entorno y la plataforma del SDK que se usa. Estos ejecutables funcionan de la misma forma que los ejecutables nativos:

  • Haga doble clic en el archivo ejecutable.
  • También puede iniciar la aplicación desde un símbolo del sistema directamente, como myapp.exe en Windows y ./myapp en Linux y macOS.

appHost y certificación de macOS

Solo para macOS

A partir del SDK de .NET Core 3.0 para macOS certificado, el valor para generar un archivo ejecutable predeterminado (conocido como appHost) está deshabilitado de forma predeterminada. Para obtener más información, vea Certificación de macOS Catalina y el impacto en las descargas y proyectos de .NET Core.

Cuando la configuración de appHost está habilitada, .NET Core genera un ejecutable Mach-O nativo al compilar o publicar. La aplicación se ejecuta en el contexto de appHost cuando se ejecuta desde el código fuente con el comando dotnet run o mediante el inicio directo del ejecutable Mach-O.

Sin appHost, la única manera en la que un usuario puede iniciar una aplicación dependiente del marco es con el comando dotnet <filename.dll>. Siempre se crea un instancia de appHost al publicar la aplicación de manera independiente.

Puede configurar appHost en el nivel de proyecto, o bien cambiar la instancia de appHost de un comando dotnet específico con el parámetro -p:UseAppHost:

  • Archivo del proyecto

    <PropertyGroup>
      <UseAppHost>true</UseAppHost>
    </PropertyGroup>
    
  • Parámetro de línea de comandos

    dotnet run -p:UseAppHost=true
    

Para obtener más información sobre la configuración de UseAppHost, vea Propiedades de MSBuild para Microsoft.NET.Sdk.

Archivos ejecutables de único archivo

El comando dotnet publish admite empaquetar la aplicación en un ejecutable de archivo único específico de la plataforma. El archivo ejecutable es autoextraíble y contiene todas las dependencias (incluidas las nativas) necesarias para ejecutar la aplicación. Cuando la aplicación se ejecuta por primera vez, se extrae en un directorio que se basa en el nombre de la aplicación y el identificador de compilación. El inicio es más rápido cuando se vuelve a ejecutar la aplicación. La aplicación no necesita extraerse por segunda vez a menos que se haya utilizado una nueva versión.

Para publicar un ejecutable de archivo único, establezca PublishSingleFile en el proyecto o en la línea de comandos con el comando dotnet publish:

<PropertyGroup>
  <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
  <PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>

o bien

dotnet publish -r win10-x64 -p:PublishSingleFile=true

Para obtener más información sobre la publicación de archivos únicos, vea el documento de diseño del programa de instalación de conjunto de archivos únicos.

Recorte de ensamblados

El SDL de .NET Core 3.0 cuenta con una herramienta que puede reducir el tamaño de las aplicaciones mediante el análisis de IL y el recorte de los ensamblados no usados.

Las aplicaciones independientes incluyen todo lo necesario para ejecutar el código, sin necesidad de instalar .NET en el equipo host. Sin embargo, muchas veces, la aplicación solo requiere un pequeño subconjunto de marco para que funcione, y otras bibliotecas que no se utilizan podrían quitarse.

.NET Core incluye ahora un valor que usará la herramienta Recortador de IL para examinar el nivel de integridad de la aplicación. Esta herramienta detecta el código que es necesario y, después, recorta las bibliotecas no utilizadas. Esta herramienta puede reducir significativamente el tamaño de implementación de algunas aplicaciones.

Para habilitar esta herramienta, agregue el valor <PublishTrimmed> en el proyecto y publique una aplicación independiente:

<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
dotnet publish -r <rid> -c Release

Por ejemplo, la nueva y básica plantilla de proyecto de consola "Hola mundo" que se incluye, cuando se publica, tiene un tamaño aproximado de 70 MB. Mediante el uso de <PublishTrimmed>, ese tamaño se reduce a unos 30 MB.

Es importante tener en cuenta que las aplicaciones o marcos (incluidos ASP.NET Core y WPF) que usan la reflexión o las características dinámicas relacionadas, se interrumpirán a menudo cuando se recorten. Esta interrupción se produce porque el recortador no conoce este comportamiento dinámico y no puede determinar qué tipos de marco son necesarios para la reflexión. La herramienta Recortador de IL puede configurarse para tener en cuenta este escenario.

Por encima de todo lo demás, no olvide probar la aplicación después del recorte.

Para obtener más información sobre la herramienta Recortador de IL, consulte la documentación o visite el repositorio mono/linker.

Compilación en niveles

La compilación en niveles (TC) está activada de forma predeterminada con .NET Core 3.0. Esta característica permite que el runtime use el compilador Just-In-Time (JIT) de forma más flexible para lograr un mejor rendimiento.

La principal ventaja de la compilación en niveles es que ofrece dos maneras de aplicar JIT a los métodos: con un nivel de menor calidad, pero más rápido, o un nivel de mayor calidad, pero más lento. La calidad se refiere al grado de optimización del método. La compilación en niveles contribuye a mejorar el rendimiento de una aplicación a medida que pasa por distintas fases de ejecución, desde el inicio hasta el estado estable. Cuando la compilación en niveles está deshabilitada, todos los métodos se compilan de una manera única que prima el rendimiento de estado estable sobre el rendimiento de inicio.

Cuando la compilación en niveles está habilitada, se aplica el comportamiento siguiente a la compilación de métodos al iniciar una aplicación:

  • Si el método tiene código compilado mediante Ahead-Of-Time, o ReadyToRun, se usa el código generado previamente.
  • De lo contrario, el método se compila mediante JIT. Normalmente, estos métodos son genéricos con respecto a los tipos de valor.
    • JIT rápido produce código de menor calidad (o menos optimizado) más rápidamente. En .NET Core 3.0, JIT rápido está habilitado de forma predeterminada para los métodos que no contienen ningún bucle y tiene preferencia durante el inicio.
    • JIT de optimización completa produce código de mayor calidad (o más optimizado) más lentamente. En el caso de los métodos en los que no se use la compilación mediante JIT rápida (por ejemplo, si el método tiene el atributo MethodImplOptions.AggressiveOptimization), se utilizará la compilación mediante JIT totalmente optimizada.

En el caso de los métodos a los que se llama con frecuencia, el compilador Just-in-Time al final crea código totalmente optimizado en segundo plano. Luego, el código optimizado reemplaza el código compilado previamente de ese método.

El código generado con compilación mediante JIT rápida puede ejecutarse más lentamente, asignar más memoria o usar más espacio de pila. Si hay problemas, puede deshabilitar JIT rápido con esta propiedad de MSBuild en el archivo de proyecto:

<PropertyGroup>
  <TieredCompilationQuickJit>false</TieredCompilationQuickJit>
</PropertyGroup>

Para deshabilitar completamente la compilación en niveles, use esta propiedad de MSBuild en el archivo de proyecto:

<PropertyGroup>
  <TieredCompilation>false</TieredCompilation>
</PropertyGroup>

Sugerencia

Si cambia esta configuración en el archivo de proyecto, es posible que deba realizar una compilación limpia para que se refleje la nueva configuración (elimine los directorios obj y bin y vuelva a compilar).

Para obtener más información sobre la configuración de la compilación en tiempo de ejecución, consulte Opciones de configuración del entorno de ejecución para compilación.

Imágenes ReadyToRun

Puede mejorar el tiempo de inicio de la aplicación .NET Core mediante la compilación de los ensamblados de aplicación como el formato ReadyToRun (R2R). R2R es una forma de compilación Ahead Of Time (AOT).

Los binarios de R2R mejoran el rendimiento de inicio reduciendo la cantidad de trabajo que el compilador Just-In-Time (JIT) debe llevar a cabo cuando se carga la aplicación. Los binarios contienen código nativo similar en comparación con lo que generaría el compilador JIT. Sin embargo, los binarios de R2R son más grandes porque contienen tanto el código de lenguaje intermedio (IL), que sigue siendo necesario para algunos escenarios, como la versión nativa del mismo código. R2R solo está disponible cuando publica una aplicación independiente que tenga como destino entornos de tiempo de ejecución específicos (RID), como Linux x64 o Windows x64.

Para compilar el proyecto como ReadyToRun, haga lo siguiente:

  1. Agregue el valor <PublishReadyToRun> al proyecto:

    <PropertyGroup>
      <PublishReadyToRun>true</PublishReadyToRun>
    </PropertyGroup>
    
  2. Publique una aplicación independiente. Por ejemplo, este comando crea una aplicación independiente para la versión de 64 bits de Windows:

    dotnet publish -c Release -r win-x64 --self-contained
    

Restricciones multiplataforma y de arquitectura

Actualmente, el compilador ReadyToRun no admite la compatibilidad cruzada. Debe compilar en un destino dado. Por ejemplo, si desea imágenes R2R para Windows x64, deberá ejecutar el comando de publicación en ese entorno.

Excepciones de la compatibilidad cruzada:

  • Windows x64 se puede usar para compilar imágenes de Windows ARM32, ARM64 y x86.
  • Windows x86 se puede usar para compilar imágenes de Windows ARM32.
  • Linux x64 se puede usar para compilar imágenes de Linux ARM32 y ARM64.

Para obtener más información, consulte Ready to Run.

Runtime y SDK

Puesta al día del runtime de versiones principales

.NET Core 3.0 presenta una característica opcional que permite poner la aplicación al día con la versión principal más reciente de .NET Core. Además, se agregó una nueva configuración para controlar cómo se aplica la puesta al día a la aplicación. Esto se puede configurar de las maneras siguientes:

  • Propiedad de archivo del proyecto: RollForward
  • Propiedad de archivo de configuración del entorno de ejecución: rollForward
  • Variable de entorno: DOTNET_ROLL_FORWARD
  • Argumento de línea de comandos: --roll-forward

Debe especificarse uno de los valores siguientes. Si se omite la configuración, Minor es el valor predeterminado.

  • LatestPatch
    Se pone al día con la última versión de revisión. Se deshabilita la puesta al día de versiones secundarias.
  • Minor
    Se pone al día con la versión secundaria mínima superior, si no se encuentra la versión secundaria solicitada. Si se encuentra la versión secundaria solicitada, se utiliza la directiva LatestPatch.
  • Major
    Se pone al día con la versión secundaria mínima superior, y la versión secundaria mínima, si no se encuentra la versión secundaria solicitada. Si se encuentra la versión principal solicitada, se utiliza la directiva Minor.
  • LatestMinor
    Se pone al día con la última versión secundaria, aunque la versión secundaria solicitada esté presente. Se destina a escenarios de hospedaje de componentes.
  • LatestMajor
    Se pone al día con la última versión principal y la última versión secundaria, aunque la versión principal solicitada esté presente. Se destina a escenarios de hospedaje de componentes.
  • Deshabilitar
    No se pone al día. Solo se enlaza a la versión especificada. No se recomienda esta directiva para uso general, ya que deshabilita la capacidad de puesta al día con las revisiones más recientes. Este valor solo se recomienda a efectos de pruebas.

Además del valor Disable, todos los valores usarán la última versión de revisión disponible.

De forma predeterminada, si la versión solicitada (como se especifica en .runtimeconfig.json para la aplicación) es una versión de lanzamiento, solo se tienen en cuenta las versiones de lanzamiento para la puesta al día. Se omiten las versiones preliminares. Si no hay ninguna versión de lanzamiento que coincida, se tienen en cuenta las versiones preliminares. Este comportamiento se puede cambiar estableciendo DOTNET_ROLL_FORWARD_TO_PRERELEASE=1, en cuyo caso siempre se tienen en cuenta todas las versiones.

Compilación de dependencias de copias

El comando dotnet build copia ahora las dependencias de NuGet para la aplicación de la caché de NuGet a la carpeta de salida de compilación. Anteriormente, las dependencias solo se copiaban como parte de dotnet publish.

Hay algunas operaciones, como la publicación de páginas Razor y el recortado, que aún es necesario publicar.

Herramientas locales

.NET Core 3.0 presenta herramientas locales. Las herramientas locales son similares a las herramientas globales pero están asociadas a una ubicación concreta en el disco. Las herramientas locales no están disponibles globalmente y se distribuyen como paquetes NuGet.

Las herramientas locales se basan en un nombre de archivo de manifiesto dotnet-tools.json del directorio actual. Este archivo de manifiesto define las herramientas que estarán disponibles en esa carpeta y a continuación. Puede distribuir el archivo de manifiesto con su código para asegurarse de que todo aquel que trabaje con su código pueda restaurar y utilizar las mismas herramientas.

Para las herramientas locales y globales, se requiere una versión compatible del entorno de ejecución. Actualmente, muchas herramientas de NuGet.org tienen como destino el entorno de ejecución de .NET Core 2.1. Para instalar estas herramientas local o globalmente, aún tendría que instalar NET Core 2.1 Runtime.

Opciones nuevas de global.json

El archivo global.json tiene opciones nuevas que proporcionan más flexibilidad cuando se intenta definir qué versión de la SDK de .NET Core se usa. Las opciones nuevas son:

  • allowPrerelease: Indica si la resolución del SDK debe tener en cuenta las versiones preliminares a la hora de seleccionar la versión del SDK que se va a usar.
  • rollForward: indica la directiva de puesta al día que se va a usar al seleccionar una versión del SDK, ya sea como reserva si falta una versión específica del SDK o como una directiva para usar una versión superior.

Para más información sobre los cambios, incluidos los valores predeterminados, los valores admitidos y reglas de coincidencia nuevas, consulte la información general de global.json.

Tamaños del montón de recolección de elementos no utilizados más pequeños

Se ha reducido el tamaño predeterminado del montón del recolector de elementos no utilizados, lo que se traduce en que .NET Core usa menos memoria. Este cambio se adapta mejor al presupuesto de asignación de generación 0 con tamaños de caché de procesador moderno.

Compatibilidad con Large Pages de recolección de elementos no utilizados

Large Pages (también conocida como Huge Pages en Linux) es una característica en la que el sistema operativo es capaz de establecer regiones de memoria más grandes que el tamaño de página nativo (a menudo, 4 K) para mejorar el rendimiento de la aplicación que solicita estas páginas grandes.

Ahora, el recolector de elementos no utilizados puede configurarse con el valor GCLargePages como característica opcional para elegir la asignación de páginas grandes en Windows.

Escritorio de Windows y COM

Windows Installer del SDK de .NET Core

El instalador MSI para Windows ha cambiado a partir de .NET Core 3.0. Los instaladores del SDK actualizarán ahora las versiones de la banda de características del SDK. Las bandas de características se definen en los grupos de centenas de la sección revisión del número de versión. Por ejemplo, 3.0.101 y 3.0.201 son versiones de dos bandas de características distintas mientras que 3.0.101 y 3.0.199 están en la misma banda de características. Y, cuando se instale el SDK 3.0.101 de .NET Core, se quitará el SDK 3.0.100 de .NET Core de la máquina si existe. Cuando se instale el SDK 3.0.200 de .NET Core en la misma máquina, no se quitará el SDK 3.0.101 de .NET Core.

Para obtener más información sobre las versiones, vea el artículo de introducción a la creación de versiones de .NET Core.

Escritorio de Windows

.NET Core 3.0 admite aplicaciones de escritorio de Windows con Windows Presentation Foundation (WPF) y Windows Forms. Estos marcos también admiten el uso de controles modernos y los estilos de Fluent de la biblioteca de XAML de la interfaz de usuario de Windows (WinUI) a través de islas XAML.

El componente Escritorio de Windows forma parte del SDK de Windows .NET Core 3.0.

Puede crear una aplicación de Windows Forms o WPF con los siguientes comandos dotnet:

dotnet new wpf
dotnet new winforms

Visual Studio 2019 agrega plantillas de nuevo proyecto para WPF y Windows Forms de .NET Core 3.0.

Para obtener más información sobre cómo migrar una aplicación existente de .NET Framework, vea los artículos sobre cómo portar proyectos de WPF y cómo portar proyectos de Windows Forms.

PPP alto de WinForms

En .NET Core, las aplicaciones de Windows Forms pueden establecer el modo de valores altos de PPP con Application.SetHighDpiMode(HighDpiMode). El método SetHighDpiMode establece el modo de valores altos de PPP correspondiente a menos que la opción se haya establecido por otros medios como App.Manifest o P/Invoke antes de Application.Run.

Los posibles valores de highDpiMode, expresados por la enumeración System.Windows.Forms.HighDpiMode son:

  • DpiUnaware
  • SystemAware
  • PerMonitor
  • PerMonitorV2
  • DpiUnawareGdiScaled

Para más información sobre los modos de valores altos de PPP, consulte el artículo sobre desarrollo de aplicaciones de escritorio con valores altos de PPP en Windows.

Creación de componentes COM

En Windows, ahora puede crear componentes COM administrados invocables. Esta capacidad es fundamental para usar .NET Core con modelos de complemento COM, así como para ofrecer paridad con .NET Framework.

A diferencia de .NET Framework, donde se utilizó mscoree.dll como servidor COM, .NET Core agregará un archivo dll de inicio nativo al directorio bin al compilar el componente COM.

Para ver un ejemplo de cómo crear un componente COM y usarlo, consulte la demostración de COM.

Interoperabilidad nativa de Windows

Windows ofrece una API nativa enriquecida en forma de API de C sin formato, COM y WinRT. Mientras que .NET Core admite P/Invoke, .NET Core 3.0 agrega la capacidad de API COM CoCreate y API WinRT Activate. Para obtener un ejemplo de código, vea la demostración de Excel.

Implementación de MSIX

MSIX es un nuevo formato de paquete de aplicación de Windows. Se puede usar para implementar aplicaciones de escritorio de .NET Core 3.0 en Windows 10.

El proyecto de paquete de aplicación de Windows, disponible en Visual Studio 2019, le permite crear paquetes de MSIX con aplicaciones de .NET Core independientes.

El archivo del proyecto de .NET Core debe especificar los tiempos de ejecución admitidos en la propiedad <RuntimeIdentifiers>:

<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>

Mejoras de Linux

SerialPort para Linux

.Net Core 3.0 proporciona compatibilidad básica para System.IO.Ports.SerialPort en Linux.

Anteriormente, .NET Core solo admitía el uso de SerialPort en Windows.

Para obtener más información sobre la compatibilidad limitada para el puerto de serie en Linux, vea Problema #33146 de GitHub.

Docker y límites de memoria de cgroup

La ejecución de .NET Core 3.0 en Linux con Docker funciona mejor con límites de memoria de cgroup. La ejecución de un contenedor de Docker con límites de memoria, como con docker run -m, cambia el comportamiento de .NET Core.

  • Tamaño predeterminado del montón del recolector de elementos no utilizados (GC): máximo de 20 MB o 75 % del límite de memoria en el contenedor.
  • Puede establecerse el tamaño explícito como número absoluto o porcentaje del límite de cgroup.
  • El tamaño mínimo del segmento reservado por el montón de GC es de 16 MB. Con este tamaño se reduce el número de montones que se crean en las máquinas.

Compatibilidad de GPIO con Raspberry Pi

Se han publicado dos paquetes en NuGet que puede usar para la programación de GPIO:

Los paquetes GPIO incluyen interfaces API para dispositivos GPIO, SPI, I2C y PWM. El paquete de enlaces de IoT incluye enlaces de dispositivos. Para obtener más información, vea el repositorio de GitHub de los dispositivos.

Compatibilidad con ARM64 para Linux

.NET Core 3.0 agrega compatibilidad con ARM64 para Linux. El principal caso de uso de ARM64 son escenarios de IoT. Para obtener más información, vea el artículo sobre el estado de ARM64 de .NET Core.

Hay imágenes de docker para .NET Core en ARM64 disponibles para Alpine, Debian y Ubuntu.

Nota

La compatibilidad con los sistemas operativos macOS ARM64 (o "Apple Silicon") y Windows ARM64 se agregó más adelante en .NET 6.

Seguridad

TLS 1.3 y OpenSSL 1.1.1 en Linux

.NET Core aprovecha ahora la ventaja de la compatibilidad con TLS 1.3 en OpenSSL 1.1.1, cuando está disponible en un entorno determinado. Con TLS 1.3:

  • Se han mejorado los tiempos de conexión con menores recorridos de ida y vuelta necesarios entre el cliente y servidor.
  • Se ha mejorado la seguridad gracias a la eliminación de varios algoritmos criptográficos obsoletos y no seguros.

Cuando está disponible, .NET Core 3.0 utiliza OpenSSL 1.1.1, OpenSSL 1.1.0 o OpenSSL 1.0.2 en un sistema Linux. Si OpenSSL 1.1.1 está disponible, los tipos System.Net.Security.SslStream y System.Net.Http.HttpClient, utilizarán TLS 1.3 (suponiendo que el cliente y el servidor admitan TLS 1.3).

El siguiente ejemplo de C# 8.0 muestra .NET Core 3.0 en Ubuntu 18.10 al conectarse a https://www.cloudflare.com:

using System;
using System.Net.Security;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace whats_new
{
    public static class TLS
    {
        public static async Task ConnectCloudFlare()
        {
            var targetHost = "www.cloudflare.com";

            using TcpClient tcpClient = new TcpClient();

            await tcpClient.ConnectAsync(targetHost, 443);

            using SslStream sslStream = new SslStream(tcpClient.GetStream());

            await sslStream.AuthenticateAsClientAsync(targetHost);
            await Console.Out.WriteLineAsync($"Connected to {targetHost} with {sslStream.SslProtocol}");
        }
    }
}

Cifrados de criptografía

.NET Core 3.0 agrega compatibilidad con los cifrados AES-GCM y AES-CCM, que se implementan con System.Security.Cryptography.AesGcm y System.Security.Cryptography.AesCcm respectivamente. Estos dos algoritmos son algoritmos AEAD (Authenticated Encryption with Associated Data).

El código siguiente muestra cómo utilizar cifrado AesGcm para cifrar y descifrar datos aleatorios.

using System;
using System.Linq;
using System.Security.Cryptography;

namespace whats_new
{
    public static class Cipher
    {
        public static void Run()
        {
            // key should be: pre-known, derived, or transported via another channel, such as RSA encryption
            byte[] key = new byte[16];
            RandomNumberGenerator.Fill(key);

            byte[] nonce = new byte[12];
            RandomNumberGenerator.Fill(nonce);

            // normally this would be your data
            byte[] dataToEncrypt = new byte[1234];
            byte[] associatedData = new byte[333];
            RandomNumberGenerator.Fill(dataToEncrypt);
            RandomNumberGenerator.Fill(associatedData);

            // these will be filled during the encryption
            byte[] tag = new byte[16];
            byte[] ciphertext = new byte[dataToEncrypt.Length];

            using (AesGcm aesGcm = new AesGcm(key))
            {
                aesGcm.Encrypt(nonce, dataToEncrypt, ciphertext, tag, associatedData);
            }

            // tag, nonce, ciphertext, associatedData should be sent to the other part

            byte[] decryptedData = new byte[ciphertext.Length];

            using (AesGcm aesGcm = new AesGcm(key))
            {
                aesGcm.Decrypt(nonce, ciphertext, tag, decryptedData, associatedData);
            }

            // do something with the data
            // this should always print that data is the same
            Console.WriteLine($"AES-GCM: Decrypted data is {(dataToEncrypt.SequenceEqual(decryptedData) ? "the same as" : "different than")} original data.");
        }
    }
}

Importación y exportación de claves criptográfica

.NET Core 3.0 admite la importación y exportación de claves asimétricas públicas y privadas en formatos estándar. No es necesario utilizar un certificado X.509.

Todos los tipos de clave, como RSA, DSA, ECDsa y ECDiffieHellman, admiten los siguientes formatos:

  • Clave pública

    • SubjectPublicKeyInfo X.509
  • Clave privada

    • PrivateKeyInfo PKCS#8
    • EncryptedPrivateKeyInfo PKCS#8

Las claves RSA también admiten:

  • Clave pública

    • RSAPublicKey PKCS#1
  • Clave privada

    • RSAPrivateKey PKCS#1

Los métodos de exportación generan datos binarios con codificación DER y los métodos de importación esperan lo mismo. Si una clave se almacena en el formato PEM de texto descriptivo, el llamador debe descodificar en base64 el contenido antes de llamar a un método de importación.

using System;
using System.Security.Cryptography;

namespace whats_new
{
    public static class RSATest
    {
        public static void Run(string keyFile)
        {
            using var rsa = RSA.Create();

            byte[] keyBytes = System.IO.File.ReadAllBytes(keyFile);
            rsa.ImportRSAPrivateKey(keyBytes, out int bytesRead);

            Console.WriteLine($"Read {bytesRead} bytes, {keyBytes.Length - bytesRead} extra byte(s) in file.");
            RSAParameters rsaParameters = rsa.ExportParameters(true);
            Console.WriteLine(BitConverter.ToString(rsaParameters.D));
        }
    }
}

Los archivos PKCS#8 se pueden inspeccionar con System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo y los archivos PFX/PKCS#12 se pueden inspeccionar con System.Security.Cryptography.Pkcs.Pkcs12Info. Los archivos PFX/PKCS#12 se pueden manipular con System.Security.Cryptography.Pkcs.Pkcs12Builder.

Cambios de API en .NET Core 3.0

Rangos e índices

El nuevo tipo System.Index se puede utilizar para la indización. Puede crear uno desde un índice int que cuente desde el principio o con un operador ^ de prefijo (C#) que cuente desde el final:

Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"

Existe también el tipo System.Range, que consta de dos valores Index, uno para el inicio y otro para el final, y se puede escribir con una expresión de intervalo x..y (C#). Luego puede crear un índice con un Range, lo que genera un segmento:

var slice = a[i1..i2]; // { 3, 4, 5 }

Para obtener más información, vea el tutorial sobre intervalos e índices.

Flujos asincrónicos

El tipo IAsyncEnumerable<T> es una nueva versión asincrónica de IEnumerable<T>. El lenguaje permite ejecutar la instrucción await foreach en IAsyncEnumerable<T> para consumir sus elementos, y usar la instrucción yield return en ellos para generar los elementos.

En el ejemplo siguiente se muestra la producción y el consumo de flujos asincrónicos. La instrucción foreach es asincrónica y usa yield return para generar un flujo asincrónico para los llamadores. Este patrón (que usa yield return) es el modelo recomendado para generar flujos asincrónicos.

async IAsyncEnumerable<int> GetBigResultsAsync()
{
    await foreach (var result in GetResultsAsync())
    {
        if (result > 20) yield return result;
    }
}

Además de poder ejecutar await foreach, también puede crear iteradores asincrónicos, por ejemplo, uno que devuelva un enumerador IAsyncEnumerable/IAsyncEnumerator en el que pueda ejecutar await y yield. Para los objetos que deban eliminarse, puede usar IAsyncDisposable, que implementan varios tipos BCL, como Stream y Timer.

Para obtener más información, vea el tutorial sobre flujos asincrónicos.

Punto flotante de IEEE

Las API de punto flotante se actualizan para cumplir con la revisión IEEE 754-2008. El objetivo de estos cambios es exponer todas operaciones requeridas y asegurarse de que cumplen con la especificación IEEE. Para obtener más información sobre las mejoras de punto flotante, vea la entrada de blog sobre mejoras de formato y análisis de punto flotante en .NET Core 3.0.

Entre las correcciones de análisis y formato se incluyen:

  • Analice y redondee entradas de cualquier longitud correctamente.
  • Analice y formatee el cero negativo correctamente.
  • Análisis correcto de Infinity y NaN al hacer una comprobación que no distingue mayúsculas de minúsculas y permitir un + anterior opcional cuando corresponda.

Entre las nuevas API System.Math se incluyen:

  • BitIncrement(Double) y BitDecrement(Double)
    Corresponde a las operaciones IEEE nextUp y nextDown. Devuelven el número de punto flotante más pequeño que compara mayor o menor que la entrada (respectivamente). Por ejemplo, Math.BitIncrement(0.0) devolvería double.Epsilon.

  • MaxMagnitude(Double, Double) y MinMagnitude(Double, Double)
    Corresponde a las operaciones IEEE maxNumMag y minNumMag, que devuelven el valor que es mayor o menor en magnitud de las dos entradas (respectivamente). Por ejemplo, Math.MaxMagnitude(2.0, -3.0) devolvería -3.0.

  • ILogB(Double)
    Corresponde a la operación IEEE logB que devuelve un valor entero, devuelve el logaritmo en base 2 integral del parámetro de entrada. Este método es efectivamente el mismo que floor(log2(x)), pero con errores de redondeo mínimo.

  • ScaleB(Double, Int32)
    Corresponde a la operación IEEE scaleB que toma un valor integral, devuelve eficazmente x * pow(2, n), pero se realiza con errores de redondeo mínimo.

  • Log2(Double)
    Corresponde a la operación IEEE log2. Devuelve el logaritmo de base 2. Minimiza el error de redondeo.

  • FusedMultiplyAdd(Double, Double, Double)
    Corresponde a la operación IEEE fma. Realiza una multiplicación y suma fusionadas. Es decir, realiza (x * y) + z como operación única, de forma que se minimiza el error de redondeo. Un ejemplo es FusedMultiplyAdd(1e308, 2.0, -1e308), que devuelve 1e308. La operación (1e308 * 2.0) - 1e308 regular devuelve double.PositiveInfinity.

  • CopySign(Double, Double)
    Corresponde a la operación IEEE copySign. Devuelve el valor de x, pero con el signo de y.

Elementos intrínsecos dependientes de la plataforma .NET

Se han agregado API que permiten el acceso a determinadas instrucciones CPU orientadas al rendimiento, como los conjuntos de instrucciones de manipulación de bits o SIMD. Estas instrucciones pueden ayudar a conseguir importantes mejoras de rendimiento en ciertos escenarios, como el procesamiento de datos con eficiencia en paralelo.

En su caso, las bibliotecas de .NET han comenzado a utilizar estas instrucciones para mejorar el rendimiento.

Para obtener más información, consulte el artículo sobre elementos intrínsecos dependientes de la plataforma .NET.

API de versión mejoradas de .NET Core

A partir de .NET Core 3.0, las API de versión incluidas con .NET Core ahora devuelven la información que se espera. Por ejemplo:

System.Console.WriteLine($"Environment.Version: {System.Environment.Version}");

// Old result
//   Environment.Version: 4.0.30319.42000
//
// New result
//   Environment.Version: 3.0.0
System.Console.WriteLine($"RuntimeInformation.FrameworkDescription: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}");

// Old result
//   RuntimeInformation.FrameworkDescription: .NET Core 4.6.27415.71
//
// New result (notice the value includes any preview release information)
//   RuntimeInformation.FrameworkDescription: .NET Core 3.0.0-preview4-27615-11

Advertencia

Cambio importante. Se trata técnicamente de un cambio importante, porque ha cambiado el esquema de control de versiones.

Compatibilidad con JSON integrada con rápido rendimiento

Los usuarios de .NET se han basado en gran medida en Newtonsoft.Json y otras bibliotecas populares de JSON, que siguen siendo buenas opciones. Newtonsoft.Json usa cadenas de .NET como tipo de datos base, que, en esencia, es UTF-16.

La nueva compatibilidad integrada con JSON es de alto rendimiento, baja asignación y funciona con texto JSON codificado UTF-8. Para obtener más información sobre el espacio de nombres System.Text.Json y los tipos, vea los artículos siguientes:

Compatibilidad con HTTP/2

El tipo System.Net.Http.HttpClient es compatible con el protocolo HTTP/2. Si se habilita HTTP/2, la versión del protocolo HTTP se negocia a través de TLS/ALPN y HTTP/2 solo se usa si el servidor opta por usarlo.

El protocolo predeterminado sigue siendo HTTP/1.1, pero se puede habilitar HTTP/2 de dos maneras diferentes. En primer lugar, puede establecer el mensaje de solicitud HTTP para usar HTTP/2:

var client = new HttpClient() { BaseAddress = new Uri("https://localhost:5001") };

// HTTP/1.1 request
using (var response = await client.GetAsync("/"))
    Console.WriteLine(response.Content);

// HTTP/2 request
using (var request = new HttpRequestMessage(HttpMethod.Get, "/") { Version = new Version(2, 0) })
using (var response = await client.SendAsync(request))
    Console.WriteLine(response.Content);

En segundo lugar, puede cambiar HttpClient para usar HTTP/2 de forma predeterminada:

var client = new HttpClient()
{
    BaseAddress = new Uri("https://localhost:5001"),
    DefaultRequestVersion = new Version(2, 0)
};

// HTTP/2 is default
using (var response = await client.GetAsync("/"))
    Console.WriteLine(response.Content);

Muchas veces cuando está desarrollando una aplicación, desea utilizar una conexión no cifrada. Si sabe que el punto de conexión de destino utilizará HTTP/2, puede activar las conexiones no cifradas para HTTP/2. Puede activarlas estableciendo la variable de entorno DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2UNENCRYPTEDSUPPORT en 1 o habilitándolas en el contexto de la aplicación:

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

Pasos siguientes