Novidades do .NET Core 3.0

Este artigo descreve as novidades do .NET Core 3.0. Um dos maiores avanços é o suporte para aplicativos Windows de área de trabalho (somente Windows). Usando a Área de Trabalho do Windows do componente de SDK do .NET Core 3.0, você pode portar seus aplicativos Windows Forms e WPF (Windows Presentation Foundation). Para deixar claro, o componente Windows Desktop só é compatível com o Windows e só é incluído nele. Para obter mais informações, consulte a seção Área de Trabalho do Windows mais adiante neste artigo.

O .NET Core 3.0 adiciona suporte para C# 8.0. É altamente recomendável que você use o Visual Studio 2019 versão 16.3 ou mais recente ou o Visual Studio Code com a extensão C# mais recente.

Baixe e comece a usar o .NET Core 3.0 versão agora no Windows, macOS ou Linux.

Para obter mais informações sobre a versão, consulte o comunicado do .NET Core 3.0.

O .NET Core 3.0 RC é considerado uma produção pronta pela Microsoft e tem suporte total. Se você estiver usando uma versão prévia, deverá passar para a versão RTM para dar suporte contínuo.

Aprimoramentos de linguagem C# 8.0

O C# 8.0 também faz parte dessa versão, que inclui o recurso de tipos de referência anuláveis, fluxos assíncronos e mais padrões. Para obter mais informações sobre recursos do C# 8.0, consulte Novidades do C# 8.0.

Tutoriais relacionados aos recursos de linguagem C# 8.0:

Foram adicionados aprimoramentos de linguagem para dar suporte aos seguintes recursos de API detalhados abaixo:

.NET Standard 2.1

O .NET Core 3.0 implementa o .NET Standard 2.1. No entanto, o modelo dotnet new classlib padrão gera um projeto que ainda tem como destino .NET Standard 2.0. Para direcionar ao .NET Standard 2.1, edite seu arquivo de projeto e altere a propriedade TargetFramework para netstandard2.1:

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

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

</Project>

Se você estiver usando o Visual Studio, precisará do Visual Studio 2019, já que o Visual Studio 2017 não dá suporte ao .NET Standard 2.1 nem ao .NET Core 3.0.

Compilar/Implantar

Executáveis por padrão

O .NET Core agora compila executáveis dependentes de estrutura por padrão. Esse comportamento é novo para aplicativos que usam uma versão do .NET Core instalada globalmente. Anteriormente, apenas implantações autocontidas produziam um executável.

Durante dotnet build ou dotnet publish, é criado um executável (conhecido como o appHost) que corresponde ao ambiente e à plataforma do SDK que você está usando. Você pode esperar desses executáveis o mesmo que de outros executáveis nativos, como:

  • Você pode clicar duas vezes no arquivo executável.
  • Você pode iniciar o aplicativo diretamente de um prompt de comando, como myapp.exe no Windows e ./myapp no Linux e macOS.

appHost e notarização no macOS

Somente macOS

A partir do SDK 3.0 do .NET Core em cartório para macOS, a configuração para produzir um executável padrão (conhecido como appHost) é desabilitada por padrão. Para obter mais informações, consulte Notarização do macOS Catalina e o impacto nos downloads e projetos do .NET Core.

Quando a configuração appHost está habilitada, o .NET Core gera um executável Mach-O nativo quando você cria ou publica. Seu aplicativo é executado no contexto do appHost quando ele é executado do código-fonte com o comando dotnet run ou iniciando o executável Mach-O diretamente.

Sem o appHost, a única maneira de um usuário iniciar um aplicativo dependente de estrutura é com o comando dotnet <filename.dll>. Um appHost sempre é criado quando você publica seu aplicativo autocontido.

Você pode configurar o appHost no nível do projeto ou alternar o appHost para um comando dotnet específico com o parâmetro -p:UseAppHost:

  • Arquivo de projeto

    <PropertyGroup>
      <UseAppHost>true</UseAppHost>
    </PropertyGroup>
    
  • Parâmetro de linha de comando

    dotnet run -p:UseAppHost=true
    

Para obter mais informações sobre a configuração UseAppHost, consulte Propriedades do MSBuild para Microsoft.NET.Sdk.

Executáveis de arquivo único

O comando dotnet publish dá suporte ao empacotamento de seu aplicativo em um executável de arquivo único específico da plataforma. O executável é autoextraível e contém todas as dependências (incluindo nativas) necessárias para a execução do aplicativo. Quando o aplicativo é executado pela primeira vez, o aplicativo é extraído para um diretório com base no nome do aplicativo e no identificador do build. A inicialização é mais rápida quando o aplicativo é executado novamente. O aplicativo não precisará autoextrair uma segunda vez, a menos que uma versão nova tenha sido usada.

Para publicar um único arquivo executável, defina o PublishSingleFile em seu projeto ou na linha de comando com o comando dotnet publish:

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

- ou -

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

Para obter mais informações sobre a publicação de arquivo único, consulte o documento de design de empacotador de arquivo único.

Corte de assembly

O SDK do .NET Core 3.0 vem com uma ferramenta que pode reduzir o tamanho dos aplicativos analisando a IL e cortando assemblies não utilizados.

Os aplicativos autossuficientes incluem todos os componentes necessários para executar seu código, sem exigir que o .NET seja instalado no computador host. No entanto, muitas vezes o aplicativo requer apenas um pequeno subconjunto da estrutura para funcionar, e outras bibliotecas não utilizadas podem ser removidas.

O .NET Core agora inclui uma configuração que usará a ferramentaVinculador de IL para verificar a IL do seu aplicativo. Essa ferramenta detecta qual código é necessário e, em seguida, corta bibliotecas não usadas. Ela pode reduzir significativamente o tamanho da implantação de alguns aplicativos.

Para habilitá-la, adicione a configuração <PublishTrimmed> ao seu projeto e publique um aplicativo autossuficiente:

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

Por exemplo, o modelo básico de novo projeto de console "hello world" incluído, quando publicado, atinge cerca de 70 MB de tamanho. Usando <PublishTrimmed>, esse tamanho é reduzido para cerca de 30 MB.

É importante considerar que os aplicativos ou estruturas (incluindo ASP.NET Core e WPF) que usam reflexão ou recursos dinâmicos relacionados, geralmente são interrompidos quando cortados. Essa interrupção ocorre porque o cortador não tem ciência desse comportamento dinâmico e não pode determinar quais tipos de estrutura são necessários para reflexão. A ferramenta Cortador de IL pode ser configurada para estar ciente deste cenário.

Antes de mais nada, teste seu aplicativo depois de cortar.

Para saber mais sobre a ferramenta Cortador de IL, confira a documentação ou visite o repositório mono/linker.

Compilação em camadas

A TC (compilação em camadas) está ativa por padrão com o .NET Core 3.0. Esse recurso permite que o runtime use de modo mais adaptável o compilador JIT (just-in-time) para conseguir um melhor desempenho.

O principal benefício da compilação em camadas é fornecer duas maneiras de jitting de métodos: em uma camada de baixa qualidade, mas mais rápida, ou em uma camada de maior qualidade, mas mais lenta. A qualidade refere-se ao quão bem o método é otimizado. O TC ajuda a aumentar o desempenho de um aplicativo quando ele passa por vários estágios da execução, desde a inicialização até o estado estável. Quando a compilação em camadas é desabilitada, cada método é compilado de uma única maneira que é tendenciosa para o desempenho de estado estável em relação ao desempenho de inicialização.

Quando o TC está habilitado, o seguinte comportamento se aplica à compilação de método quando um aplicativo é iniciado:

  • Se o método tiver um código compilado antecipadamente ou ReadyToRun, o código pré-gerado será usado.
  • Caso contrário, o método será jitted. Normalmente, esses métodos são genéricos sobre tipos de valor.
    • O JIT rápido produz o código de baixa qualidade (ou menos otimizado) mais rapidamente. No .NET Core 3.0, o JIT Rápido é habilitado por padrão para métodos que não contêm loops e é preferencial durante a inicialização.
    • O JIT de otimização completa produz código de maior qualidade (ou mais otimizado) mais lentamente. Para métodos em que o Quick JIT não seria usado (por exemplo, se o método for atribuído com MethodImplOptions.AggressiveOptimization), o JIT de otimização completa é usado.

Para métodos chamados com frequência, o compilador just-in-time eventualmente cria um código totalmente otimizado em segundo plano. Em seguida, o código otimizado substitui o código pré-compilado para esse método.

O código gerado pelo Quick JIT pode ser executado mais lento, alocar mais memória ou usar mais espaço de pilha. Se houver problemas, você poderá desabilitar o JIT Rápido usando essa propriedade MSBuild no arquivo de projeto:

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

Para desabilitar completamente a TC, use esta configuração em seu arquivo de projeto:

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

Dica

Se você alterar essas configurações no arquivo de projeto, talvez seja necessário executar um build limpo para que as novas configurações sejam refletidas (exclua os diretórios obj e bin e recompile).

Para obter mais informações sobre como configurar a compilação em tempo de execução, confira Opções de configuração do Runtime para compilação.

Imagens ReadyToRun

Você pode melhorar o tempo de inicialização do seu aplicativo .NET Core compilando seus assemblies de aplicativos como o formato ReadyToRun (R2R). R2R é uma forma de compilação antecipada (AOT).

Os binários R2R melhoram o desempenho de inicialização reduzindo a quantidade de trabalho que o compilador just-in-time (JIT) precisa fazer à medida que seu aplicativo é carregado. Os binários contêm código nativo similar comparado ao que o JIT produziria. Entretanto, os binários R2R são maiores porque contêm código de IL (linguagem intermediária), que ainda é necessário para alguns cenários, e a versão nativa do mesmo código. O R2R só está disponível quando você publica um aplicativo autocontido que tenha como alvo um RID (Runtime Environment) específico, como o Linux x64 ou o Windows x64.

Para compilar seu projeto como ReadyToRun, faça o seguinte:

  1. Adicione a configuração <PublishReadyToRun> ao seu projeto:

    <PropertyGroup>
      <PublishReadyToRun>true</PublishReadyToRun>
    </PropertyGroup>
    
  2. Publique um aplicativo autossuficiente. Por exemplo, esse comando cria um aplicativo autossuficiente para a versão de 64 bits do Windows:

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

Restrições de plataforma cruzada/arquitetura

O compilador ReadyToRun atualmente não tem suporte para o direcionamento cruzado. Você precisa compilar em determinado destino. Por exemplo, se você quiser imagens R2R para Windows x64, será necessário executar o comando Publicar nesse ambiente.

Exceções ao direcionamento cruzado:

  • O Windows x64 pode ser usado para compilar imagens do Windows ARM32, ARM64 e x86.
  • O Windows x86 pode ser usado para compilar imagens do Windows ARM32.
  • O Linux x64 pode ser usado para compilar imagens do Linux Arm32 e Arm64.

Para obter mais informações, consulte Pronto para execução.

Runtime/SDK

Roll forward de runtime de versão principal

O .NET Core 3.0 introduz um recurso opcional que permite que seu aplicativo efetue roll forward para a versão principal mais recente do .NET Core. Adicionalmente, foi adicionada uma nova configuração para controlar como o roll forward é aplicado ao seu aplicativo. Isso pode ser configurado das seguintes maneiras:

  • Propriedade do arquivo de projeto: RollForward
  • Propriedade do arquivo de configuração de runtime: rollForward
  • Variável de ambiente: DOTNET_ROLL_FORWARD
  • Argumento de linha de comando: --roll-forward

Um dos valores a seguir precisa ser especificado. Se a configuração for omitida, Secundária será o padrão.

  • LatestPatch
    Efetuar roll forward para a versão de patch mais recente. Isso desabilita o roll forward da versão secundária.
  • Secundária
    Se a versão secundária solicitada estiver ausente, efetue roll forward para a menor versão secundária mais alta. Se a versão secundária solicitada estiver presente, a política LatestPatch será usada.
  • Principal
    Se a versão principal solicitada estiver ausente, efetuar roll forward para a versão principal mais alta e a versão secundária mais baixa. Se a versão principal solicitada está presente, a política Secundária é usada.
  • LatestMinor
    Efetuar roll forward para a versão secundária mais recente, mesmo se a versão secundária solicitada estiver presente. Destinado a cenários de hospedagem de componente.
  • LatestMajor
    Efetuar roll forward para a versão principal e a secundária mais altas, mesmo se a principal solicitada estiver presente. Destinado a cenários de hospedagem de componente.
  • Desabilitar
    Não efetuar roll forward. Associar somente à versão especificada. Essa política não é recomendada para uso geral, pois ela desabilita a capacidade de efetuar roll forward para os patches mais recentes. Esse valor só é recomendado para teste.

Com a exceção da configuração Desabilitar, todas as configurações usarão a versão de patch mais recente disponível.

Por padrão, se a versão solicitada (conforme especificado em .runtimeconfig.json para o aplicativo) for uma versão de versão, somente as versões de versão serão consideradas para roll forward. Todas as versões de pré-lançamento são ignoradas. Se não houver nenhuma versão de versão correspondente, as versões de pré-lançamento serão levadas em conta. Esse comportamento pode ser alterado pela configuração DOTNET_ROLL_FORWARD_TO_PRERELEASE=1, nesse caso, todas as versões são sempre consideradas.

O build copia dependências

O comando dotnet build agora copia as dependências do NuGet para seu aplicativo do cache NuGet para a pasta de saída de build. Anteriormente, as dependências eram copiadas apenas como parte de dotnet publish.

Há algumas operações, como corte e publicação de página do razor, que ainda exigem publicação.

Ferramentas locais

O .NET Core 3.0 apresenta ferramentas locais. Ferramentas locais são semelhantes às ferramentas globais, mas estão associadas a um local específico no disco. Ferramentas locais não estão disponíveis globalmente e são distribuídas como pacotes NuGet.

As ferramentas locais dependem de um nome de arquivo de manifesto dotnet-tools.json no seu diretório atual. Esse arquivo de manifesto define as ferramentas que estarão disponíveis nessa pasta e abaixo. Você pode distribuir o arquivo de manifesto com o seu código para garantir que qualquer pessoa que trabalha com o seu código possa restaurar e usar as mesmas ferramentas.

Para ferramentas globais e locais, é necessária uma versão compatível do runtime. Muitas ferramentas que estão atualmente em NuGet.org direcionam para o runtime do .NET Core 2.1. Para instalar essas ferramentas, de forma global ou local, você ainda precisará instalar o Runtime do NET Core 2.1.

Novas opções global.json

O arquivo global.json tem novas opções que fornecem mais flexibilidade quando você está tentando definir qual versão do SDK do .NET Core é usada. As novas opções são:

  • allowPrerelease: indica se o resolvedor do SDK deve considerar versões de pré-lançamento ao selecionar a versão do SDK a ser usada.
  • rollForward: indica a política de roll-forward a ser usada ao selecionar uma versão do SDK, seja como um fallback quando uma versão específica do SDK está ausente ou como uma diretiva para usar uma versão mais alta.

Para obter mais informações sobre as alterações, incluindo valores padrão, valores com suporte e novas regras de correspondência, consulte Visão geral do global.json.

Tamanhos menores de heap de coleta de lixo

O tamanho do heap do coletor de lixo padrão foi reduzido, resultando em menor uso de memória pelo .NET Core. Essa alteração se alinha melhor com o orçamento de alocação de geração 0 com os tamanhos de cache de processadores modernos.

Suporte de página grande de coleta de lixo

Páginas grandes (também conhecidas como páginas enormes no Linux) é um recurso em que o sistema operacional é capaz de estabelecer regiões de memória maiores do que o tamanho da página nativo (geralmente 4K) para melhorar o desempenho do aplicativo que está solicitando essas páginas grandes.

O coletor de lixo agora pode ser configurado com a configuração GCLargePages como um recurso opcional a ser escolhido para alocar páginas grandes no Windows.

Windows Desktop e COM

Windows Installer do SDK do .NET Core

O instalador MSI para Windows foi alterado do .NET Core 3.0 em diante. Os instaladores de SDK agora atualizarão versões de faixa de recurso do SDK no local. Faixas de recurso são definidas nos grupos de centenas na seção patch do número de versão. Por exemplo, 3.0.101 e 3.0.201 são versões em duas faixas de recurso diferentes, enquanto 3.0.101 e 3.0.199 estão na mesma faixa de recurso. Além disso, quando o SDK do .NET Core 3.0.101 for instalado, o SDK do .NET Core 3.0.100 será removido do computador se ele existir. Quando o SDK do .NET Core 3.0.200 for instalado no mesmo computador, o SDK do .NET Core 3.0.101 não será removido.

Para obter mais informações sobre controle de versão, consulte Visão geral de como é o controle de versão no .NET Core.

Área de trabalho do Windows

O .NET Core 3.0 dá suporte a aplicativos da Área de Trabalho do Windows usando o Windows Forms e o WPF (Windows Presentation Foundation). Essas estruturas também oferecem suporte ao uso de controles modernos e no estilo Fluent da biblioteca XAML da interface do usuário do Windows (WinUI) por meio de Ilhas XAML.

O componente Windows Desktop faz parte do SDK do .NET Core 3.0 do Windows.

É possível criar um novo aplicativo de WPF ou Windows Forms com os seguintes comandos dotnet:

dotnet new wpf
dotnet new winforms

O Visual Studio 2019 adiciona modelos de Novo Projeto ao Windows Forms e WPF no .NET Core 3.0.

Para obter mais informações sobre como portar um aplicativo existente do .NET Framework, consulte Portar projetos do WPF e Portar projetos do Windows Forms.

WinForms com DPI alto

Aplicativos do Windows Forms do .NET Core podem definir o modo de DPI alto com Application.SetHighDpiMode(HighDpiMode). O método SetHighDpiMode define o modo de DPI alto correspondente, a menos que a configuração tenha sido definida por outros meios, tais como App.Manifest ou P/Invoke antes de Application.Run.

Os valores highDpiMode possíveis, conforme expressos pelo enum System.Windows.Forms.HighDpiMode, são:

  • DpiUnaware
  • SystemAware
  • PerMonitor
  • PerMonitorV2
  • DpiUnawareGdiScaled

Para obter mais informações sobre os modos de DPI alto, confira Desenvolvimento de aplicativos de área de trabalho de DPI alto no Windows.

Criar componentes COM

No Windows, agora você pode criar componentes gerenciados que podem ser chamados por COM. Essa funcionalidade é fundamental para usar o .NET Core com modelos de suplemento do COM e também para fornecer paridade com o .NET Framework.

Ao contrário do .NET Framework em que o mscoree. dll foi usado como o servidor COM, o .NET Core adicionará uma dll de inicializador nativa ao diretório bin quando você compilar o componente COM.

Para obter um exemplo de como criar um componente COM e consumi-lo, veja a demonstração de COM.

Interoperabilidade nativa do Windows

O Windows oferece uma API nativa rica na forma de APIs C simples, COM e WinRT. Embora o .NET Core dê suporte a P/Invoke, o .NET Core 3.0 adiciona a capacidade de criar conjuntamente APIs COM e ativar APIs WinRT. Para obter um exemplo de código, consulte a demonstração do Excel.

Implantação de MSIX

MSIX é um novo formato de pacote de aplicativos do Windows. Ele pode ser usado para implantar aplicativos da área de trabalho do .NET Core 3.0 no Windows 10.

O Projeto de Empacotamento de Aplicativos do Windows, disponível no Visual Studio 2019, permite criar pacotes MSIX com aplicativos .NET Core autossuficientes.

O arquivo de projeto do .NET Core precisa especificar os runtimes compatíveis na propriedade <RuntimeIdentifiers>:

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

Aprimoramentos do Linux

SerialPort para Linux

O .NET Core 3.0 fornece suporte básico para System.IO.Ports.SerialPort no Linux.

Anteriormente, o .NET Core só dava suporte ao uso de SerialPort no Windows.

Para saber mais sobre o suporte limitado para a porta serial no Linux, confira o Problema do GitHub nº 33146.

Limites de memória do Docker e cgroup

A execução do .NET Core 3.0 no Linux com o Docker funciona melhor com limites de memória cgroup. Executar um contêiner do Docker com limites de memória, tais como docker run -m, altera o comportamento do .NET Core.

  • Tamanho de heap do GC (coletor de lixo) padrão: máximo de 20 MB ou 75% do limite de memória no contêiner.
  • O tamanho explícito pode ser definido como um número absoluto ou um percentual do limite de cgroup.
  • O tamanho mínimo do segmento reservado por heap de GC é de 16 MB. Esse tamanho reduz o número de heaps que são criados em computadores.

Suporte de GPIO para o Raspberry Pi

Foram lançados dois pacotes para o NuGet que você pode usar para programação de GPIO:

Os pacotes GPIO incluem as APIs para dispositivos GPIO, SPI, I2C e PWM. O pacote de associações de IoT inclui associações de dispositivo. Para obter mais informações, veja o repositório GitHub de dispositivos.

Suporte a Arm64 no Linux

O .NET Core 3.0 adiciona suporte para Arm64 para Linux. O principal caso de uso para Arm64 atualmente é em cenários de IoT. Para obter mais informações, consulte Status do .NET Core Arm64.

Imagens do Docker para o .NET Core Arm64 estão disponíveis para Alpine, Debian e Ubuntu.

Observação

O suporte para os sistemas operacionais macOS Arm64 (ou "Apple Silicon") e Windows Arm64 foi adicionado posteriormente ao .NET 6.

Segurança

TLS 1.3 e OpenSSL 1.1.1 no Linux

O .NET Core agora faz proveito do suporte a protocolo TLS 1.3 no OpenSSL 1.1.1 quando esse protocolo está disponível em um determinado ambiente. Com o TLS 1.3:

  • Tempos de conexão são aprimorados com um menor número de viagens de ida e volta necessárias entre o cliente e o servidor.
  • Segurança aprimorada devido à remoção de vários algoritmos criptográficos obsoletos e não seguros.

Quando disponíveis, o .NET Core 3.0 usa OpenSSL 1.1.1, 1.1.0 ou 1.0.2 em um sistema Linux. Quando o OpenSSL 1.1.1 está disponível, ambos os tipos System.Net.Security.SslStream e System.Net.Http.HttpClient usarão o protocolo TLS 1.3 (supondo que o cliente e o servidor deem suporte ao protocolo TLS 1.3).

O exemplo de C# 8.0 a seguir demonstra o .NET Core 3.0 no Ubuntu 18.10 conectando-se 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}");
        }
    }
}

Cifras de criptografia

O .NET Core 3.0 adiciona o suporte para as criptografias AES-GCM e AES-CCM, implementadas com System.Security.Cryptography.AesGcm e System.Security.Cryptography.AesCcm, respectivamente. Esses algoritmos são ambos do tipo AEAD (criptografia autenticada com os dados de associação).

O código a seguir demonstra como usar criptografia AesGcm para criptografar e descriptografar dados aleatórios.

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.");
        }
    }
}

Importar/exportar chave de criptografia

O .NET Core 3.0 dá suporte à importação e exportação de chaves públicas e privadas assimétricas de formatos padrão. Você não precisa usar um certificado X.509.

Todos os tipos principais, tais como RSA, DSA, ECDsa e ECDiffieHellman, dão suporte aos seguintes formatos:

  • Chave pública

    • X.509 SubjectPublicKeyInfo
  • Chave privada

    • PKCS nº 8 PrivateKeyInfo
    • PKCS nº 8 EncryptedPrivateKeyInfo

Chaves RSA também dão suporte a:

  • Chave pública

    • PKCS nº 1 RSAPublicKey
  • Chave privada

    • PKCS nº 1 RSAPrivateKey

Os métodos de exportação produzem dados binários codificados em DER e os métodos de importação esperam o mesmo. Se uma chave for armazenada no formato PEM compatível com texto, o chamador precisará decodificar o conteúdo em Base64 antes de chamar um método de importação.

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));
        }
    }
}

Arquivos PKCS nº 8 podem ser inspecionados com System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo e arquivos PFX/PKCS nº 12 podem ser inspecionados com System.Security.Cryptography.Pkcs.Pkcs12Info. Arquivos PFX/PKCS nº 12 podem ser manipulados com System.Security.Cryptography.Pkcs.Pkcs12Builder.

Alterações na API do .NET Core 3.0

Intervalos e índices

O novo tipo System.Index pode ser usado para indexação. É possível criar um a partir de um int que conta desde o início, ou com um operador ^ de prefixo (C#) que conta do 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"

Há também o tipo System.Range, que consiste em dois valores Index (um para o início e outro para o final) e pode ser escrito com uma expressão de intervalo x..y (C#). Em seguida, você pode indexar com um Range, o que produz uma fatia:

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

Para obter mais informações, consulte o tutorial de intervalos e índices.

Fluxos assíncronos

O tipo IAsyncEnumerable<T> é uma nova versão assíncrona de IEnumerable<T>. A linguagem permite o uso de await foreach em IAsyncEnumerable<T> para consumir seus elementos e o uso de yield return neles para produzir elementos.

O exemplo a seguir demonstra a produção e o consumo de fluxos assíncronos. A instrução foreach é assíncrona e usa yield return para produzir um fluxo assíncrono para chamadores. Esse padrão (usar yield return) é o modelo recomendado para a produção de fluxos assíncronos.

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

Além de poder await foreach, você também pode criar iteradores assíncronos, por exemplo, um iterador que retorne um IAsyncEnumerable/IAsyncEnumerator em que é possível aplicar await e yield. Para objetos que precisam ser descartados, você pode usar IAsyncDisposable, que vários tipos BCL implementam, como Stream e Timer.

Para obter mais informações, consulte o tutorial de fluxos assíncronos.

Ponto flutuante IEEE

APIs de ponto flutuante estão sendo atualizadas para entrar em conformidade com a revisão IEEE 754-2008. O objetivo dessas alterações é expor todas as operações necessárias e garantir que elas estejam em conformidade comportamental com a especificação IEEE. Para obter mais informações sobre melhorias de ponto flutuante, consulte Melhorias de análise e formatação de ponto flutuante na postagem no blog do .NET Core 3.0 .

As correções de análise e formatação incluem:

  • Analisar e arredondar corretamente entradas de qualquer tamanho.
  • Analisar e formatar corretamente o zero negativo.
  • Analisar corretamente Infinity e NaN, fazendo uma verificação sem diferenciação de maiúsculas e minúsculas e permitindo um + precedente opcional onde aplicável.

Novas APIs System.Math incluem:

  • BitIncrement(Double) e BitDecrement(Double)
    Correspondem às operações nextUp e nextDown do IEEE. Retornam o menor número de ponto flutuante que compara o valor maior ou menor que a entrada (respectivamente). Por exemplo, Math.BitIncrement(0.0) retorna double.Epsilon.

  • MaxMagnitude(Double, Double) e MinMagnitude(Double, Double)
    Correspondem às operações maxNumMag e minNumMag do IEEE; retornam o valor maior ou menor em magnitude das duas entradas (respectivamente). Por exemplo, Math.MaxMagnitude(2.0, -3.0) retorna -3.0.

  • ILogB(Double)
    Corresponde à operação logB IEEE que retorna um valor integral, ele retorna o log de base 2 integral do parâmetro de entrada. Esse método é praticamente o mesmo que floor(log2(x)), mas feito com o mínimo de erro de arredondamento.

  • ScaleB(Double, Int32)
    Corresponde à operação IEEE scaleB que usa um valor integral, ele retorna efetivamente x * pow(2, n), mas é feito com o mínimo de erro de arredondamento.

  • Log2(Double)
    Corresponde à operação log2 do IEEE; retorna o logaritmo de base 2. Minimiza o erro de arredondamento.

  • FusedMultiplyAdd(Double, Double, Double)
    Corresponde à operação fma do IEEE; executa uma adição e multiplicação fundida. Em outras palavras, realiza (x * y) + z como uma única operação, minimizando o erro de arredondamento. Um exemplo é FusedMultiplyAdd(1e308, 2.0, -1e308), que retorna 1e308. O (1e308 * 2.0) - 1e308 regular retorna double.PositiveInfinity.

  • CopySign(Double, Double)
    Corresponde à operação copySign do IEEE; retorna o valor de x, mas com o sinal de y.

Intrínsecos dependentes da plataforma .NET

Foram adicionadas APIs que permitem acesso a determinadas instruções da CPU orientadas a desempenho, como o SIMD ou conjuntos de instruções de manipulação de bits. Essas instruções podem ajudar a obter melhorias significativas de desempenho em determinados cenários, tais como processamento de dados eficiente em paralelo.

Quando apropriado, as bibliotecas .NET começaram usando estas instruções para melhorar o desempenho.

Para obter mais informações, consulte Intrínsecos dependentes da plataforma .NET.

APIs de versão aprimoradas do .NET Core

Começando com o .NET Core 3.0, as APIs de versão fornecidas com o .NET Core agora retornam as informações que você espera. Por exemplo:

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

Aviso

Alteração da falha. Isso é tecnicamente uma alteração da falha, porque o esquema de controle de versão foi alterado.

Suporte interno rápido a JSON

Usuários do .NET têm dependido basicamente de Newtonsoft.Json e outras bibliotecas JSON populares, que continuam a ser boas opções. O Newtonsoft.Json usa cadeias de caracteres do .NET como seu tipo de dados base, o qual subjacentemente é UTF-16.

O novo suporte JSON interno é de alto desempenho, baixa alocação e funciona com texto JSON codificado em UTF-8. Para obter mais informações sobre o namespace e os tipos System.Text.Json, consulte os seguintes artigos:

Suporte do HTTP/2

O tipo System.Net.Http.HttpClient dá suporte ao protocolo HTTP/2. Se o HTTP/2 estiver habilitado, a versão do protocolo HTTP é negociada via TLS/ALPN, e o HTTP/2 é usado apenas se o servidor selecionar seu uso.

O protocolo padrão permanece HTTP/1.1, mas o HTTP/2 pode ser ativado de duas maneiras diferentes. Primeiro, você pode definir a mensagem de solicitação 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);

Segundo, você pode alterar HttpClient para usar HTTP/2 por padrão:

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);

Muitas vezes, quando você está desenvolvendo um aplicativo, quer usar uma conexão não criptografada. Se você souber que o ponto de extremidade estará usando HTTP/2, poderá ativar conexões não criptografadas para HTTP/2. Você pode ativá-lo definindo a variável de ambiente DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2UNENCRYPTEDSUPPORT como 1 ou ativando-a no contexto do aplicativo:

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

Próximas etapas