Julho de 2019

Volume 34, Número 7

[C#]

.NET Reunificado: Planos da Microsoft para .NET 5

Por Mark Michaelis | Julho de 2019

O anúncio do .NET 5 pela Microsoft no Microsoft Build 2019 em maio marcou um grande passo à frente para desenvolvedores que trabalham em plataformas de área de trabalho, Web, móveis, de dispositivos e em nuvem. Na verdade, o .NET 5 é uma atualização rara de plataforma que unifica estruturas divergentes, reduz a complexidade do código e aumenta significativamente o alcance de plataforma cruzada.

Não é uma tarefa simples. A Microsoft está propondo mesclar os fluxos de código-fonte de várias estruturas principais: .NET Framework, .NET Core e Xamarin/Mono. A iniciativa vai até unificar threads separados desde o início, na virada do século, e oferecer aos desenvolvedores uma estrutura de destino única para seu trabalho.

O conceito de fluxo do código-fonte na Figura 1 mostra como a linha do tempo de cada estrutura é sincronizada e acaba se mesclando em um único thread como .NET 5 em novembro de 2020. (Observe que o .NET Framework foi abreviado como .NET FW na imagem.) Quando for lançado, o .NET 5 engolirá o .NET Framework 4.8, o Mono 5.0 e o .NET Core 3.0.

Conceito de fluxo de código-fonte da Iniciativa de Fonte Compartilhada, .NET e Mono para o .NET 5
Figura 1 Conceito de fluxo de código-fonte da Iniciativa de Fonte Compartilhada, .NET e Mono para o .NET 5 (clique para ver a versão ampliada)

É verdade, a Figura 1 é mais conceitual do que a realidade, mais com bifurcação de código-fonte (provavelmente cópia) do que ramificação e mais migração de recursos (como o WPF, Windows Presentation Foundation) do que mesclagem. Mesmo assim, o infográfico oferecer uma visão razoavelmente clara do histórico do código-fonte do .NET, mostrando sua evolução dos três maiores branches até o .NET 5.

O resultado desse trabalho é uma plataforma unificada, com a estrutura do .NET 5 em execução em todas as plataformas (área de trabalho, Web, nuvem, móvel e assim por diante). A Figura 2 mostra essa arquitetura unificada.

.NET 5 – uma plataforma unificada
Figura 2. .NET 5 – uma plataforma unificada

História sobre a origem

É fascinante como as diferentes estruturas do .NET, como a Iniciativa Comum de Código-Fonte Compartilhado da Microsoft (Rotor), o SilverLight, o Windows Phone, o .NET Core e o .NET Framework (mas não o Mono), foram compilados originalmente com base no mesmo código-fonte. Em outras palavras, todo o código-fonte foi mantido em um mesmo repositório e compartilhado por todas as estruturas do .NET. Com isso, a Microsoft pôde garantir que APIs comuns às diferentes estruturas viessem do mesmo código-fonte e tivessem as mesmas assinaturas. A única diferença era quais APIs eram compartilhadas. (Observe o uso de “estrutura” do .NET, em minúsculas, que se refere a todas as estruturas do .NET em geral, e .NET “Framework”, que se refere ao Windows .NET Framework, como o .NET Framework 4.8.)

Para conseguir uma única fonte para uso em várias estruturas, foram usadas várias técnicas de subconjuntos, como vários #ifdefs. Também é interessante ver como um conjunto ortogonal de técnicas de subconjunto (ou seja, #ifdefs diferentes) foi colocado no código-fonte do .NET para criar o suporte à plataforma cruzada do Rotor, permitindo o desenvolvimento rápido tanto do Silverlight quanto (bem mais tarde) do .NET Core usando a mesma base de código.

Embora o conjunto ortogonal de técnicas de subconjunto permaneça, aquele que permitiu a compilação em plataforma cruzada, a divisão em subconjuntos para produzir as várias estruturas, está sendo removido. (Consulte, por exemplo, a solicitação de pull em bit.ly/2WdSzv2 que remove vários #ifdefs que estão obsoletos.) O motivo pelo qual é possível removê-los atualmente é que dias depois do lançamento do .NET 1.1, os códigos-fonte do .NET Core e do .NET Framework foram bifurcados (provavelmente copiados). Isso, na verdade, explica por que existem sites de código-fonte do .NET diferentes: .NET Framework em referencesource.microsoft.com e .NET Core em source.dot.net. Existem duas bases de código diferentes.

A decisão de bifurcar em vez de continuar usando subconjuntos reflete a tensão entre manter a compatibilidade com versões anteriores (uma grande prioridade do .NET Framework) e inovar (a prioridade do .NET Core). Havia simplesmente muitos casos em que a manutenção da compatibilidade entrava em conflito com a correção ou o aprimoramento das APIs do .NET, de forma que o código-fonte teve que ser separado para alcançar as duas metas. O uso de #ifdefs não era mais uma maneira funcional de separar as estruturas quando, de fato, as APIs eram diferentes e incompatíveis com as versões.

No entanto, outro conflito apareceu ao longo do tempo, o de permitir que os desenvolvedores criassem bibliotecas que poderiam ser executadas com êxito nas duas estruturas. Para isso, era necessário ter um padrão de API para garantir aos desenvolvedores que uma estrutura poderia ter um conjunto de APIs específico identificado pelo padrão. Assim, se eles aproveitassem apenas as APIs no padrão, suas bibliotecas poderiam ser compatíveis entre estruturas (o mesmo assembly poderia ser executado em estruturas diferentes sem a necessidade de recompilar).

A cada novo recurso adicionado ao .NET (por exemplo, Span<T>), foi ficando mais difícil manter a compatibilidade com versões anteriores mais antigas da estrutura. Mais especificamente, o desafio era dar suporte a conceitos em novas versões do .NET Standard, que eram realmente inovações recentes do .NET Core, no .NET Framework. Além disso, embora com menos relevância, cada nova versão do .NET Standard incluía um conjunto cada vez maior de APIs até se tornar um estorvo de manutenção para manter a compatibilidade do .NET Standard entre o .NET Framework e o .NET Core.

Juntos

As duas estruturas começaram a se tornar mais parecidas por causa do padrão. Conforme as APIs foram ficando mais consistentes, a pergunta mais óbvia começou a ser feita: por que não juntar novamente as bases de código? E, de fato, a partir da versão prévia do .NET Core 3.0, tanta coisa do .NET Framework WPF e da API do Windows foi selecionada e mesclada na base de código do .NET Core 3.0 que foi exatamente isso o que aconteceu. O código-fonte do .NET Core 3.0 ficou igual à funcionalidade atual (desktop, nuvem, móvel e IoT) do .NET Framework 4.8.

Até aqui ainda há uma grande estrutura do .NET que ainda não abordei: Mono/Xamarin. Embora o código-fonte do Rotor tenha ficado disponível publicamente, seu uso violaria o contrato de licença. O Mono começou como uma iniciativa de desenvolvimento nova com o intuito de criar uma versão do .NET compatível com Linux. A estrutura Mono continuou a crescer ao longo do tempo, até que a empresa (Ximian) foi adquirida pela Novell em 2003 e fechada oito anos depois com a venda da Novell para a Attachmate. A gestão do Ximian foi reformulada rapidamente em maio de 2011 como Xamarin. E, menos de dois anos depois, a Xamarin desenvolveu uma base de código de interface do usuário interplataformas que podia ser executada no Android e no iOS, aproveitando uma versão de plataforma cruzada e código fechado do Mono nos bastidores.

Em 2016, a Microsoft adquiriu a Xamarin para trazer todo o código-fonte das estruturas do .NET sob o controle da mesma empresa. Logo em seguida, o Mono e o SDK do Xamarin foram lançados como software livre.

Isso nos traz até a primeira metade de 2019, com basicamente duas bases de código a partir de agora: .NET Core 3.0 e Mono/Xamarin. (Embora a Microsoft vá dar suporte ao .NET Framework 4.8 no Windows até segunda ordem, o .NET Core 3.0 e o .NET 5 que virá depois vão eclipsá-lo como plataforma mais estratégica para novos aplicativos.) Além disso há o .NET Standard e a unificação das APIs no .NET Standard 2.1 que será lançado brevemente.

Mais uma vez aparece a pergunta: como as APIs ficando cada vez mais próximas, por que não mesclar o .NET Core 3.0 com o Mono? Essa iniciativa já começou, na verdade. Atualmente, o Mono já é um terço fonte do Mono, um terço CoreFx e um terço fonte de referência do .NET Framework. Isso criou o ambiente desejado para que o .NET 5 fosse anunciado no Microsoft Build 2019.

As vantagens do .NET 5

Essa versão unificada do .NET 5 dará suporte a todos os tipos de aplicativo .NET: Xamarin, ASP.NET, IoT e área de trabalho. Além disso, ela aproveitará uma única CoreFX/BCL (biblioteca de classes base), dois tempos de execução e bases de código de tempo de execução diferentes (já que é realmente difícil ter uma única fonte para dois tempos de execução que precisam ser essencialmente diferentes) e uma única cadeia de ferramentas (como a CLI do .NET). O resultado será a uniformidade entre comportamentos, APIs e experiências de desenvolvedor. Por exemplo, em vez de ter três implementações das APIs System.*, haverá um único conjunto de bibliotecas que são executadas em cada uma das plataformas.

Existem várias vantagens na unificação do .NET. A unificação da estrutura, dos tempos de execução e dos conjuntos de ferramentas de desenvolvedor em uma única base de código resultará na redução da quantidade de código duplicado que os desenvolvedores (tanto da Microsoft quanto da comunidade) precisarão manter e expandir. Além disso, como já costumamos esperar da Microsoft hoje em dia, todo o código-fonte do .NET 5 será software livre.

Com a mesclagem, vários recursos exclusivos de cada estrutura ficarão disponíveis para todas as plataformas. Por exemplo, os tipos csproj dessas plataformas serão unificados no formato de arquivo csproj do .NET Core simples e adorado por todos. Assim, um tipo de projeto do .NET Framework poderá aproveitar o formato de arquivo csproj do .NET Core. Embora os arquivos csproj do Xamarin e do .NET Framework (incluindo WPF e Windows Forms) exijam uma conversão para os formatos de arquivo csproj do .NET Core, a tarefa é semelhante à conversão do ASP.NET para o ASP.NET Core. Felizmente, hoje em dia é mais fácil ainda fazer isso graças a ferramentas como ConvertProjectToNETCore3 (consulte bit.ly/2W5Lk3D).

Outra área com uma diferença relevante é o comportamento no tempo de execução do Xamarin e do .NET Core/.NET Framework. O Xamarin usa um modelo de compilação estática, com compilação AOT (ahead-of-time) que compila o código-fonte até o código-fonte nativo da plataforma. Já o .NET Core e o .NET Framework usam compilação JIT (just-in-time). Felizmente, com o .NET 5, os dois modelos terão suporte, dependendo do destino do tipo de projeto.

Por exemplo, você pode escolher compilar seu projeto do .NET 5 em um único executável que usará o compilador JIT (jitter) no tempo de execução ou um compilador nativo para funcionar nas plataformas iOS ou Android. A maioria dos projetos vai usar jitter, mas para iOS todo o código é AOT. Para o Blazor no lado do cliente, o tempo de execução é WASM (Web Assembly), e a Microsoft quer que a AOT compile uma pequena quantidade de código gerenciado (em torno de 100 KB a 300 KB) e o resto será interpretado. (O código AOT é grande, ou seja, o custo de transmissão é bem alto.)

No .NET Core 3.0, você pode compilar em um único executável, mas ele será, na verdade, uma versão compactada de todos os arquivos necessários para a execução no tempo de execução. Quando você executa o arquivo, ele primeiro se expande em um diretório temporário e executa o ponto de entrada do aplicativo no diretório que contém todos os arquivos. Já o .NET 5 criará um arquivo executável único e real que pode ser executado diretamente no local.

Outro recurso incrível do .NET 5 é a interoperabilidade com o código-fonte de Java e Objective-C (incluindo Swift). Esse recurso é do Xamarin desde as primeiras versões, mas se estenderá a todos os projetos do .NET 5. Você poderá incluir arquivos jar em arquivos csproj, por exemplo, e poderá chamar diretamente de seu código .NET para código Java ou Objective-C. (Infelizmente, o suporte ao Objective-C provavelmente virá depois do suporte ao Java.) Observe que a interoperabilidade entre .NET 5 e Java/Objective-C só é esperada na comunicação em processo. A comunicação distribuída a outros processos no mesmo computador ou o mesmo processo em um computador diferente provavelmente exigirão serialização em uma invocação distribuída baseada em REST ou RPC.

O que não está no .NET 5

Embora exista um conjunto significativo de APIs disponíveis na estrutura do .NET 5, ele não inclui tudo o que pode ter sido desenvolvido nos últimos 20 anos ou mais. É de se esperar que todas as APIs identificadas no .NET Standard 2.1 virão a ter suporte, mas algumas das APIs mais “herdadas”, incluindo Web Forms, servidor WCF (Windows Communication Foundation) e Windows Workflow, não. Elas provavelmente ficarão apenas no .NET Framework. Se quiser ter as mesmas funcionalidades no .NET 5, pense em fazer a portabilidade dessas APIs da seguinte forma:

  • ASP.NET Web Forms => ASP.NET Blazor
  • Servidor WCF e comunicação remota => gRPC
  • Windows Workflow (WF) => Core WF (github.com/UiPath/corewf)

A falta de suporte ao servidor WCF é, com certeza, decepcionante para alguns. No entanto, a Microsoft decidiu recentemente lançar o software com uma licença de software livre MIT, e seu destino fica sob controle da comunidade (consulte github.com/CoreWCF/CoreWCF). Ainda há muito trabalho a ser feito para o lançamento independentemente do .NET Framework, mas, por enquanto, as APIs WCF no lado do cliente estão disponíveis (consulte github.com/dotnet/wcf).

Uma declaração de intenção

Mesmo que a Microsoft tenha planos de unificar suas estruturas de desenvolvedor no .NET 5, a empresa anunciou que está adotando uma cadência regular para suas versões do .NET unificadas (consulte a Figura 3). De agora em diante, você pode esperar o lançamento de versões de disponibilidade geral do .NET no quarto trimestre de cada ano. Dessas versões, cada segunda versão será uma versão LTS (Long Term Support), à qual a Microsoft dará suporte por no mínimo três anos ou por um anos depois do lançamento da LTS posterior, o que durar mais. Em outras palavras, você sempre terá pelo menos três anos para fazer upgrade de seu aplicativo para a versão LTS seguinte. Consulte bit.ly/2Kfkkw0 para saber mais sobre a política de suporte do .NET Core e o que é provável que se torne a política de suporte do .NET 5 e do que vier depois.

Cronograma de lançamento do .NET
Figura 3 Cronograma de lançamento do .NET

Até o momento, o .NET 5 é apenas um comunicado; uma declaração de intenção. Há muito trabalho a ser feito. Mesmo assim, o comunicado é importantíssimo. Quando o .NET Core foi lançado pela primeira vez, a meta era oferecer uma versão do .NET de plataforma cruzada que pudesse dar destaque ao Azure (talvez principalmente as partes de PaaS (Plataforma como um serviço) do Azure e o suporte ao .NET no Linux e em contêineres do Linux).

No conceito original, a ideia de que todo o .NET Framework pudesse ser movido para o .NET Core não era considerada realista. Mas na época do lançamento do .NET Core 2.0, isso começou a mudar. A Microsoft percebeu que precisava definir um padrão de estrutura para todas as versões de estrutura do .NET, para permitir que o código em execução em uma estrutura pudesse passar para outra.

Esse padrão, claro, ficou conhecido como o .NET Standard. Seu objetivo era identificar qual API precisava de suporte de determinada estrutura, para que as bibliotecas no padrão pudessem ter a certeza de que um conjunto específico de APIs estaria disponível para elas. No fim das contas, com a definição do padrão e a implementação com Xamarin/Mono, o .NET Framework se tornou um componente-chave que permitiu a estratégia de unificação do .NET 5.

Por exemplo, depois que cada estrutura implementou código que dava suporte ao conjunto de APIs do .NET Standard, pareceu lógico trabalhar para unir as diferentes bases de código (uma espécie de refatoração). E quando o comportamento não é o mesmo (compilações AOT e JIT, por exemplo), por que não mesclar o código para que todas as plataformas deem suporte às duas abordagens e aos dois recursos? A iniciativa não é simples, mas o resultado é um grande passo rumo à redução da complexidade e da manutenção, ao mesmo tempo unificando os recursos em todas as plataformas.

É um pouco irônico pensar que o próprio .NET Standard que possibilitou a unificação provavelmente ficará irrelevante com isso. Na verdade, com o surgimento do .NET 5, é pouco provável que haverá outra versão do .NET Standard; o .NET 5 e cada versão posterior dele serão o padrão.

Conclusão

Dizem que timing é tudo, e isso é verdade em relação ao .NET 5. Uma recodificação virtualmente abrangente do .NET Framework não era nem possível de se cogitar quando o desenvolvimento do .NET Core começou. Naquela época, a Microsoft estava atendendo à demanda de aprimorar significativamente a experiência de hospedagem do Azure no Linux, em contêineres e em PaaS. Assim, a empresa estava totalmente concentrada em criar algo para atender às demandas dos clientes e da equipe de produto do Azure.

Com o .NET Core 2.0, a missão foi expandida para ter as funcionalidades correspondentes do .NET Framework. Mais uma vez, a equipe estava totalmente concentrada em lançar algo de viável em vez de dar o passo maior do que a perna. Mas tudo começou a mudar com o .NET Core 3.0 e a implementação do .NET Standard 2.1. A ideia de ter que fazer alterações em três estruturas distintas quando um novo recurso ou bug aparecia era um estorvo e uma despesa. E, como para qualquer bom desenvolvedor, surgiu a ideia de refatorar o código ao máximo em uma única base de código.

Assim nasceu o .NET 5. E junto com ele nasceu a ideia de unificar todos os recursos de cada estrutura, independentemente de ser formatos csproj simples, adoção de modelos de desenvolvimento de software livre, habilitação de interoperabilidade com Java e Objective-C (incluindo Swift) ou suporte a compilações JIT e AOT. E, assim, a ideia de uma estrutura unificada se tornou obviamente a próxima etapa, uma que, espero, será celebrada por todos dentro e fora da Microsoft.


Mark Michaelis é fundador da IntelliTect, onde atua como arquiteto técnico principal e instrutor. Há quase 20 anos trabalha como Microsoft MVP, e é Diretor Regional da Microsoft desde 2007. Michaelis atua em diversas equipes de análise de design de software da Microsoft, incluindo C#, Microsoft Azure, SharePoint e Visual Studio ALM. Ele apresenta palestras em conferências de desenvolvedores e escreveu diversos livros, incluindo o mais recente, “Essential C# 7.0 (6ª edição)” (itl.tc/EssentialCSharp). Você pode contatá-lo pelo Facebook, em facebook.com/Mark.Michaelis, pelo seu blog IntelliTect.com/Mark, no Twitter: @markmichaelis ou pelo email mark@IntelliTect.com.

Agradecemos aos seguintes especialistas técnicos da Microsoft pela revisão deste artigo: Rich Lander


Discuta esse artigo no fórum do MSDN Magazine