.NET Conf e .NET Core - Resumo

O .NET Core 3.0 foi lançado oficialmente em setembro de 2019, durante a .NET Conf, o evento online da Microsoft dedicado à plataforma. Neste artigo, eu resumo as principais novidades da versão e deixo alguns vídeos, tanto dos lançamentos quanto de uma live que fizemos sobre o assunto.

Além disso, cada seção explica como usar o recurso na prática, com exemplos de configuração baseados na documentação oficial.

O que chegou no .NET Core 3.0

O .NET Core 3.0 foi uma das maiores atualizações da plataforma até então. Ele trouxe suporte a aplicações desktop no Windows, uma nova versão da linguagem C# e diversas melhorias de publicação e de performance.

A documentação oficial destaca o suporte a Windows Forms e WPF como um dos maiores avanços da versão. No entanto, a lista vai muito além do desktop e passa por Linux, Docker, IoT e segurança.

Para aproveitar tudo, a Microsoft recomendava o Visual Studio 2019 versão 16.3 ou superior. Também era possível usar o Visual Studio Code com a extensão de C# atualizada.

Suporte ao C# 8.0

O C# é uma das minhas linguagens favoritas e, com certeza, uma das melhores e mais completas do mercado. Embora a linguagem já esteja bem madura, sempre chegam novidades boas, e ela alcançou a versão 8.0 com suporte do .NET Core 3.0.

Entre os destaques do C# 8.0 estão os nullable reference types, os async streams, os ranges e índices e novos padrões de pattern matching. Por exemplo, os índices e ranges simplificam o acesso a partes de um array:

int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

Index third = 3;   // terceira posição a partir do início
Index fourthFromEnd = ^4; // quarta posição a partir do fim

var slice = numbers[third..fourthFromEnd]; // { 3, 4, 5 }

Já os async streams permitem consumir sequências assíncronas com await foreach. Dessa forma, o código que processa dados sob demanda fica muito mais legível.

async IAsyncEnumerable<int> GetLargeResultsAsync(IAsyncEnumerable<int> results)
{
    await foreach (var result in results)
    {
        // Devolve apenas os valores maiores que 20
        if (result > 20) yield return result;
    }
}

Se você quer entender melhor a parte de nulabilidade, vale ler o artigo sobre nullable types no .NET.

.NET Standard 2.1

Com a chegada do .NET Core 3.0 também veio o .NET Standard 2.1, uma especificação de API que padroniza o que cada implementação do .NET oferece. Para usar o .NET Standard 2.1, basta definir netstandard2.1 no Target Framework do arquivo de projeto.

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

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

</Project>

Vale um detalhe: o template dotnet new classlib ainda gerava projetos com o .NET Standard 2.0. Portanto, a troca para a versão 2.1 precisava ser feita manualmente.

Quer saber o que cada versão do .NET Standard suporta? Então consulte a documentação oficial do .NET Standard.

Publicação e performance no .NET Core 3.0

Boa parte das novidades do .NET Core 3.0 mira a forma como publicamos e distribuímos aplicações. Em seguida, veja os recursos que mudaram o processo de deploy.

Executável único

Agora podemos gerar o executável em um único arquivo. Chega de pastas e vários arquivos para suas aplicações, já que tudo pode ficar em um só.

Para isso, adicione a propriedade PublishSingleFile ao arquivo de projeto, junto do runtime de destino:

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

Outra opção é usar o comando de publicação diretamente:

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

No .NET Core 3.0, esse executável se extrai na primeira execução para uma pasta baseada no nome e na build da aplicação. Por isso, as execuções seguintes iniciam mais rápido, porque a extração não se repete.

Assemblies menores com PublishTrimmed

Outra novidade é a propriedade PublishTrimmed, que analisa o IL da aplicação e remove os assemblies não utilizados. Ela funciona em aplicações self-contained, que levam o runtime junto.

<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>

Segundo a documentação, um "hello world" de console publicado ocupa cerca de 70 MB. Com o PublishTrimmed, esse tamanho cai para cerca de 30 MB.

Contudo, aplicações que usam reflexão podem quebrar depois do trimming, porque a ferramenta não enxerga esse comportamento dinâmico. Então, teste sempre a aplicação depois de ativar o recurso.

ReadyToRun no .NET Core 3.0

Também podemos melhorar o tempo de inicialização das aplicações .NET Core 3.0 com o formato ReadyToRun (R2R). Ele é uma forma de compilação AOT, ou seja, Ahead of Time.

Para usar o recurso, basta adicionar a propriedade PublishReadyToRun ao arquivo de projeto e publicar uma aplicação self-contained:

<PropertyGroup>
  <PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
dotnet publish -c Release -r win-x64 --self-contained

Os binários R2R reduzem o trabalho do compilador JIT durante a carga da aplicação. Por outro lado, eles ficam maiores, porque guardam o IL e a versão nativa do mesmo código.

Tiered compilation

O .NET Core 3.0 também liga por padrão a tiered compilation. Com ela, o runtime compila primeiro uma versão rápida e menos otimizada dos métodos e, depois, recompila os métodos mais chamados com otimização completa.

Na prática, isso melhora tanto a inicialização quanto o desempenho em regime contínuo. Caso surja algum problema, você pode desligar o recurso com a propriedade TieredCompilation igual a false.

Build, dependências e ferramentas locais

Agora, no build, todas as dependências externas do NuGet são copiadas para a pasta de saída da aplicação. Antes, isso acontecia apenas no dotnet publish.

Ainda assim, algumas operações, como o trimming e a publicação de páginas Razor, continuam exigindo o comando de publicação.

Outra novidade são as local tools. Assim como temos ferramentas globais, agora podemos ter ferramentas disponíveis apenas para uma aplicação específica.

Essas ferramentas dependem de um arquivo de manifesto chamado dotnet-tools.json. Dessa forma, você distribui o manifesto junto do código, e qualquer pessoa da equipe restaura e usa as mesmas versões.

# Cria o manifesto e instala uma ferramenta local
dotnet new tool-manifest
dotnet tool install dotnet-ef

Além disso, o .NET Core 3.0 permite que a aplicação avance para a versão major mais recente do runtime instalado. Esse comportamento é opcional e você o configura com a propriedade RollForward.

Windows Forms e WPF no .NET Core 3.0

Finalmente chegou o suporte a aplicações Windows Forms e WPF no .NET Core 3.0, apenas no Windows. O Visual Studio 2019 também recebeu atualizações para suportá-las, além de dois novos comandos:

dotnet new wpf
dotnet new winforms

Com isso, aplicações desktop antigas, feitas no .NET Framework, ganharam um caminho de migração para o .NET Core. Além disso, elas passaram a usar controles modernos do WinUI por meio das XAML Islands.

O Windows Forms também ganhou o método Application.SetHighDpiMode, que define o modo de alta resolução da aplicação. Assim, as telas ficam nítidas em monitores com DPI elevado.

Pacotes MSIX

O MSIX é um novo formato de pacote de aplicativos do Windows. Ele permite instalar aplicações Windows Forms e WPF do .NET Core 3.0 no Windows 10.

Para gerar o pacote, o Visual Studio 2019 oferece o Windows Application Packaging Project. Nesse caso, o arquivo de projeto precisa listar os runtimes suportados:

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

Além disso, o MSIX empacota aplicações self-contained. Ou seja, o usuário final não precisa instalar o runtime do .NET Core separadamente para executar o aplicativo.

HTTP/2 e JSON nativo

O System.Net.Http.HttpClient agora vem com suporte ao HTTP/2. O protocolo padrão continua sendo o HTTP/1.1, mas você pode ativar o HTTP/2 em cada requisição ou como padrão do cliente.

var client = new HttpClient
{
    BaseAddress = new Uri("https://localhost:5001"),
    // Usa HTTP/2 como versão padrão das requisições
    DefaultRequestVersion = new Version(2, 0)
};

using var response = await client.GetAsync("/");
Console.WriteLine(response.Version);

Além disso, o .NET Core 3.0 trouxe o System.Text.Json, uma biblioteca de JSON nativa, de alta performance e com poucas alocações. Ela trabalha diretamente com texto em UTF-8, enquanto o Newtonsoft.Json usa strings UTF-16.

Docker e Linux no .NET Core 3.0

Houve melhorias significativas no consumo de memória e no tamanho das aplicações .NET Core 3.0 em máquinas Linux. Em uma das demos do evento, foi apresentada uma imagem Alpine com uma aplicação .NET Core 3 e o runtime instalado ocupando apenas 88 MB.

O runtime também passou a respeitar melhor os limites de memória definidos no Docker com docker run -m. Segundo a documentação, o heap padrão do GC fica no máximo entre 20 MB e 75% do limite do container.

Se você está começando com containers, o artigo sobre Docker, instalação, configuração e primeiros passos ajuda a montar o ambiente.

Suporte a ARM64 no Linux

O .NET Core 3.0 adiciona suporte a ARM64 no Linux. Esse foi um grande avanço para a criação de aplicações para processadores ARM64, principalmente em cenários de IoT.

Além disso, a Microsoft disponibilizou imagens Docker do .NET Core para ARM64 com Alpine, Debian e Ubuntu.

Suporte de GPIO para o Raspberry Pi

Foram lançados dois pacotes no NuGet para programação de GPIO: System.Device.Gpio e Iot.Device.Bindings. Eles trazem APIs para dispositivos GPIO, SPI, I2C e PWM.

O pacote de bindings, por sua vez, inclui drivers prontos para diversos sensores e componentes. Assim, fica bem mais simples controlar hardware a partir de um Raspberry Pi com C#.

dotnet add package System.Device.Gpio
dotnet add package Iot.Device.Bindings

Segurança e criptografia no .NET Core 3.0

O .NET Core 3.0 também passou a usar o TLS 1.3 no Linux quando o OpenSSL 1.1.1 está disponível. Com isso, as conexões ficam mais rápidas e mais seguras, porque o protocolo remove algoritmos obsoletos.

A versão ainda adicionou as cifras AES-GCM e AES-CCM, por meio das classes AesGcm e AesCcm. Por fim, ela passou a importar e exportar chaves assimétricas em formatos padrão, como PKCS#8, sem exigir um certificado X.509.

Outras mudanças nas APIs

As APIs de versão também foram corrigidas. Antes, Environment.Version retornava algo como 4.0.30319.42000, enquanto agora ela mostra a versão real do runtime.

// No .NET Core 3.0, o resultado passa a ser 3.0.0
Console.WriteLine($"Environment.Version: {Environment.Version}");

A documentação classifica essa correção como breaking change, porque o esquema de versionamento mudou. Portanto, revise qualquer lógica que dependa desse valor.

Além disso, as APIs de ponto flutuante passaram a seguir a revisão IEEE 754-2008. Com isso, chegaram métodos como Math.FusedMultiplyAdd, Math.Log2 e Math.CopySign, que reduzem erros de arredondamento.

Por fim, a versão trouxe intrínsecos dependentes de plataforma, que dão acesso a instruções SIMD do processador. Na prática, as próprias bibliotecas do .NET passaram a usá-los para ganhar performance.

Live sobre o .NET Core 3.0

Logo depois do evento, fizemos uma live no ASP.NET Cast comentando as novidades do .NET Core 3.0. Nela, discutimos o impacto de cada recurso no dia a dia de quem desenvolve.

Clique aqui para assistir ao resumo do evento.

Se você quiser colocar a mão na massa com a versão seguinte, o artigo criando uma API do zero com .NET Core 3.1 e EF Core mostra um projeto completo.

Vídeos que gostei da .NET Conf

A .NET Conf teve dezenas de sessões, e algumas delas se destacaram para mim. Por isso, separei a lista abaixo com os vídeos que mais valem o seu tempo:

Em especial, as sessões de Blazor ajudam a entender o rumo que o desenvolvimento web com C# tomou nos anos seguintes.

Conclusão

O .NET Core 3.0 marcou a chegada do desktop ao .NET Core, além de trazer o C# 8.0 e diversas melhorias de publicação. Recursos como executável único, trimming e ReadyToRun mudaram a forma de distribuir aplicações.

Além disso, as melhorias em Docker, Linux, ARM64 e IoT mostraram que a plataforma estava pronta para qualquer ambiente. Portanto, vale assistir aos vídeos da .NET Conf para entender em detalhes cada novidade do .NET Core 3.0.

FAQ

Quando o .NET Core 3.0 foi lançado?

O .NET Core 3.0 foi lançado em setembro de 2019, durante a .NET Conf. A versão trouxe o C# 8.0 e o suporte a Windows Forms e WPF.

O .NET Core 3.0 roda Windows Forms no Linux?

Não. O suporte a Windows Forms e WPF no .NET Core 3.0 funciona apenas no Windows.

Qual versão do Visual Studio usar com o .NET Core 3.0?

A Microsoft recomendava o Visual Studio 2019 versão 16.3 ou superior. O Visual Studio 2017 não suporta o .NET Core 3.0 nem o .NET Standard 2.1.

Como gerar um executável único no .NET Core 3.0?

Use dotnet publish -r win10-x64 /p:PublishSingleFile=true ou adicione PublishSingleFile ao arquivo de projeto. É preciso informar o runtime de destino.

O que é ReadyToRun?

É um formato de compilação antecipada que reduz o trabalho do JIT na inicialização. Em troca, os binários ficam maiores.