Introdução
A cada lançamento, o C# adiciona recursos que nos ajudam a tornar nossos códigos mais limpos, mais legíveis e mais fáceis de manter. O problema é que, como alguns recursos dependem de implementações de tempo de execução, as versões do C# geralmente estão vinculadas às versões do tempo de execução do .NET. Por exemplo, o C# 11 é habilitado apenas no .NET 7 e superior.
Neste post, mostrarei como usar o C# 11 em versões de tempo de execução mais antigas (até mesmo .NET Framework 2.0).
Por que não atualizar a versão do .NET?
Atualizar para a versão mais recente do .NET é a melhor opção. Não apenas nos beneficiamos de novos recursos do C#, mas também de melhorias de desempenho e segurança.
Mas existem alguns cenários em que a atualização não é uma opção devido à compatibilidade ou porque o custo da atualização seria muito alto.
Alguns exemplos são:
- Funções AWS Lambda em execução no .NET 6 (AWS Lambda não oferece suporte a .NET 7 no momento deste post);
- Plugins ou extensões para software proprietário, como plugins Dynamics/Dataverse que não são compatíveis com .NET Core
- Sistemas legados com uma grande base de código que ainda recebem atualizações frequentes.
Quais recursos do C# 11 podem ser usados?
Os recursos do C# são divididos em recursos que exigem suporte de tempo de execução e recursos que são apenas açúcar sintático.
Recursos que exigem suporte de tempo de execução não podem ser usados em versões .NET mais antigas, mas a maioria dos recursos que são açúcar sintático são compilados para IL (Linguagem Intermediária .NET) e interpretados por versões .NET mais antigas em tempo de execução (até mesmo .NET Framework 2.0), dependendo apenas de uma versão atualizada do Roslyn (o compilador .NET) para funcionar.
Como usar recursos do C# 11 no .NET 6 e versões anteriores
Alguns recursos funcionarão apenas instalando o SDK do .NET 7 e adicionando (ou atualizando) a tag LangVersion
para 11
no arquivo csproj
.
<LangVersion>11</LangVersion>
Exemplos
Aqui estão alguns exemplos dos recursos mais úteis das versões mais recentes do C# (não apenas C# 11).
Instruções de nível superior
Não há necessidade de static void Main
:
|
|
Tipos de referência anuláveis
Este é um exemplo funcional de tipos de referência anuláveis e instruções de nível superior no .NET Framework 2.0:
|
|
💡 Podemos até tratar avisos como erros, como expliquei neste post.
ℹ️ Se o projeto não usar o novo formato csproj, o
<Nullable>enable</Nullable>
não será interpretado e o uso da diretiva#nullable enable
no início de cada arquivo é obrigatório.
⚠️ Uma ressalva de usar tipos anuláveis em versões .NET mais antigas é que as funções do framework não nos informarão se retornam tipos de referência anuláveis porque essas alterações foram implementadas apenas nas versões .NET mais recentes.
EDIT: Um leitor entrou em contato comigo sobre este pacote legal que resolve parcialmente este problema injetando anotações de tipo de referência anulável nos métodos CLR de algumas assemblies (verifique os documentos para mais detalhes): ReferenceAssemblyAnnotator.
Correspondência de padrões
A correspondência de padrões também funciona no .NET Framework 2.0:
|
|
⚠️ A correspondência de padrões de lista não funcionará apenas alterando a tag
LangVersion
. Ele precisa de tipos específicos que explicarei na próxima seção.
Recursos que precisam de tipos específicos
Mesmo para recursos que são açúcar sintático, alguns dependem de tipos e atributos implementados nos CLRs mais recentes (por exemplo, a correspondência de padrões de lista e a palavra-chave required).
Se copiarmos esses tipos do código-fonte CLR ou referenciá-los de pacotes NuGet, a compilação será bem-sucedida e os recursos estarão disponíveis.
Mas existe uma alternativa melhor…
Apresentando PolySharp
PolySharp é um pacote NuGet criado por Sergio Pedri, Engenheiro de Software da Microsoft, que gera polyfills para esses tipos em tempo de compilação, apenas para os recursos que estão sendo usados no código e que não estão presentes no tempo de execução de destino.
Recursos habilitados pelo PolySharp
Esta é uma lista abreviada de alguns recursos C# habilitados pelo PolySharp:
- Atributos de nulidade
- Índice e Intervalo
- Correspondência de padrões de lista
- Membros obrigatórios
- Propriedades somente init
- [CallerArgumentExpression]
- [StringSyntax]
Para instalá-lo, basta adicionar seu pacote NuGet:
Install-Package PolySharp
⚠️ Como o PolySharp usa geradores de origem, ele não funciona com o arquivo
package.config
, conforme declarado nesta issue. A issue diz que precisamos usar o.csproj
estilo SDK, mas apenas mudar depackage.config
paraPackage Reference
funcionou para mim.No Visual Studio, clique com o botão direito do mouse em
References
e selecioneMigrate package.config to Package Reference
, confirme as alterações e terminamos:
Palavra-chave Required, propriedades somente init e registros
Aqui está um exemplo de um aplicativo .NET Framework 4.7.2 usando a palavra-chave required, propriedades somente init e registros:
|
|
Se inspecionarmos o RequiredMemberAttribute
e IsExternalInit
, podemos ver que eles foram gerados pelo PolySharp:
⚠️ Os tipos de registro exigem tempos de execução .NET Framework 4 ou superiores.
Código fonte dos exemplos
https://github.com/dgenezini/CSharpNewestFeatures