Esta publicação está disponível em vídeo, ampliada e revisada, no canal da EximiaCo.
As próximas versões do compilador da linguagem C# irão fornecer, finalmente, suporte a metaprogramação. Trata-se de uma novidade importante que vai permitir, em diversos cenários, ganhos consideráveis de produtividade no desenvolvimento e de performance na execução das aplicações.
O suporte a metaprogramação será oferecido através de uma nova feature até então conhecida como Source Generators.
Source Generators
Um Source Generator é um código escrito para ser executado durante o processo de compilação e que consegue “inspecionar” o programa sendo compilado, produzindo artefatos adicionais para serem compilados junto.
Trata-se de um novo tipo de componente que desenvolvedores conseguem produzir permitindo:
- Obter um objeto Compilation com todas as informações sobre o código que está sendo compilado. Este objeto pode ser inspecionado e permite que sejam produzidos artefatos considerando a sintaxe e modelos semânticos.
- Gerar código C# para ser adicionado ao objeto Compilation. Em outras palavras, é possível adicionar código-fonte extra como input para o processo de compilação que está sendo executado.
Quando combinadas, essas duas capacidades se mostram realmente úteis. Podemos inspecionar o código fonte sendo compilado, com informações ricas obtidas durante a compilação para, então, gerar código novo.
Um Source Generator é uma classe simples, escrita por qualquer programador, que será instanciada e executada automaticamente durante o processo da compilação.
Uma versão em preview do Visual Studio já integra a execução dos source generators com a escrita do código tornando a experiência fluída.
public class CsvLineGenerator : ISourceGenerator { public void Execute(SourceGeneratorContext context) { throw new NotImplementedException(); } public void Initialize(InitializationContext context) { throw new NotImplementedException(); } }
Com source generators será possível, por exemplo, escrever funcionalidades similares aos Type Providers do F#, acelerando o desenvolvimento pela geração automática de tipos, por exemplo, a partir de arquivos CSV, JSON, XML e HTML, reduzindo, significativamente, tanto custos de desenvolvimento quanto de manutenção.
Muito do código que hoje é escrito de forma manual e repetitiva poderá ser gerado automaticamente a partir de “pistas” deixadas pelos desenvolvedores no código. Um exemplo, extraído da página de referência da funcionalidade, que demonstra bem esse cenário, é a escrita de código para implementar a interface INotifyPropertyChanged
.
using AutoNotify; public partial class UserClass { [AutoNotify] private bool _boolProp; [AutoNotify(PropertyName = "Count")] private int _intProp; }
No exemplo, bastará o programador utilize um atributo como “pista” para que um source generator, durante o processo de compilação, escreva o código apropriado.
public partial class UserClass : INotifyPropertyChanged { public bool BoolProp { get => _boolProp; set { _boolProp = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("UserBool")); } } public int Count { get => _intProp; set { _intProp = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); } } public event PropertyChangedEventHandler PropertyChanged; }
As vantagens evidentes incluem, obviamente, menos código explicitamente escrito pelos programadors no dia a dia, bem como garantias de implementação de comportamento padrão que reduz, inclusive, a necessidade de escritas de testes.
Source Generators seguramente também irão reduzir a necessidade de reflection, que, embora seja um recurso extremamente poderoso, tem custo extremamente elevado em runtime.
Bem implementados, source generators podem gerar grande economia. Entretanto, será importante atentar para o risco de uso demasiado que pode aumentar as dificuldades para gestão dos times – afinal, será necessário capacitar as pessoas para, além de entender o domínio, entender quais source generators são adotados e em que situações.
A funcionalidade está ainda em preview e deverá estar disponível, junto com C# 9, até o final de 2020.
Só não entendo como eu referencio uma property que só existe em tempo de compilação no meu código “normal”. Ele sabe o que vai ser gerado e não da erros? Vc testou como funciona isso?
No Visual Studio 2019 Preview, ele roda os Source Generators na mesma lógica dos Code Analyzers. Então, o código fonte novo é gerado e considerado enquanto você escreve o código.
Nossa que legal, muito muito show.
Como fica a questão do debugging e breakpoints?
Até agora, debugging e breakpoints para código local funciona plenamente. Código gerado pelos Source Generators, até onde vi, é tratado com código externo.
Elemar, isso não se compara a algumas características que o CodeDOM permite fazer no .NET Framework?
Não. A ideia aqui é permitir mudar os executáveis durante a compilação.
Daria para dizer que o Source Generators atuaria como uma espécie de delegate?
Não. Delegates apontam para uma função, determinada assinatura, para ser executada. Source Generators é código que roda durante o processo de compilação.
1) O código gerado pelo Source Generator pode ser analisado por outros Source Generators para produzir outros arquivos de código fonte? 2) Será que o Source Generator vai causar dor de cabeça para alguns desenvolvedores no sentido de não conseguir identificar o motivo de a compilação estar tão lenda? “Ah! Você está usando esse source generator aqui que está deixando o processo de compilação mais lenta.” 3) Acho que talvez fosse bom ter na IDE uma opção para visualizar os arquivos gerados em alguma “pasta” no solution explorer. Mesmo que fossem read-only. Ajudaria no problema de on-boarding do projeto.