Compartilhar via


Analisadores Roslyn e biblioteca com reconhecimento de código para ImmutableArrays

A plataforma de compilador .NET ("Roslyn") ajuda você a criar bibliotecas com reconhecimento de código. Uma biblioteca com reconhecimento de código fornece funcionalidade que você pode usar e ferramentas (analisadores Roslyn) para ajudá-lo a usar a biblioteca da melhor maneira ou para evitar erros. Este tópico mostra como criar um analisador Roslyn do mundo real para capturar erros comuns ao usar o pacote NuGet System.Collections.Immutable . O exemplo também demonstra como fornecer uma correção de código para um problema de código encontrado pelo analisador. Os usuários veem correções de código na interface do usuário da lâmpada do Visual Studio e podem aplicar uma correção para o código automaticamente.

Introdução

Você precisa do seguinte para criar este exemplo:

  • Visual Studio 2015 (não uma Express Edition) ou uma versão posterior. Você pode usar o Visual Studio Community Edition gratuito
  • SDK do Visual Studio. Você também pode, ao instalar o Visual Studio, verificar Ferramentas de extensibilidade do Visual Studio em Ferramentas comuns para instalar o SDK ao mesmo tempo. Se você já tiver instalado o Visual Studio, você também pode instalar esse SDK indo para o menu principal Arquivo>Novo>Projeto, escolhendo C# no painel de navegação esquerdo e, em seguida, escolhendo Extensibilidade. Quando você escolhe o modelo de projeto de trilha "Instalar as ferramentas de extensibilidade do Visual Studio", ele solicita que você baixe e instale o SDK.
  • SDK da Plataforma de Compilador .NET ("Roslyn"). Você também pode instalar esse SDK acessando o menu principal Arquivo>Novo>Projeto, escolhendo C# no painel de navegação esquerdo e escolhendo Extensibilidade. Quando você escolhe o modelo de projeto de trilha "Download the .NET Compiler Platform SDK", ele solicita que você baixe e instale o SDK. Este SDK inclui o Roslyn Syntax Visualizer. Essa ferramenta útil ajuda você a descobrir quais tipos de modelo de código você deve procurar em seu analisador. A infraestrutura do analisador chama seu código para tipos de modelo de código específicos, para que seu código só seja executado quando necessário e possa se concentrar apenas na análise de código relevante.

Qual é o problema?

Imagine que você forneça uma biblioteca com suporte a ImmutableArray (por exemplo, System.Collections.Immutable.ImmutableArray<T>). Os desenvolvedores de C# têm muita experiência com matrizes .NET. No entanto, devido à natureza de ImmutableArrays e técnicas de otimização usadas na implementação, as intuições do desenvolvedor C# fazem com que os usuários de sua biblioteca escrevam código quebrado, conforme explicado abaixo. Além disso, os usuários não veem seus erros até o tempo de execução, que não é a experiência de qualidade que eles estão acostumados no Visual Studio com .NET.

Os usuários estão familiarizados com a escrita de código como o seguinte:

var a1 = new int[0];
Console.WriteLine("a1.Length = {0}", a1.Length);
var a2 = new int[] { 1, 2, 3, 4, 5 };
Console.WriteLine("a2.Length = {0}", a2.Length);

A criação de matrizes vazias para preencher com linhas de código subsequentes e o uso da sintaxe do inicializador de coleção são familiares aos desenvolvedores de C#. No entanto, escrever o mesmo código para um ImmutableArray falha em tempo de execução:

var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = {0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = {0}", b2.Length);

O primeiro erro é devido à implementação ImmutableArray usando uma struct para encapsular o armazenamento de dados subjacente. As structs devem ter construtores sem parâmetros para que default(T) as expressões possam retornar structs com todos os membros zero ou nulos. Quando o código acessa b1.Lengtho , há um erro de desreferenciação nula em tempo de execução porque não há nenhuma matriz de armazenamento subjacente na estrutura ImmutableArray. A maneira correta de criar um ImmutableArray vazio é ImmutableArray<int>.Empty.

O erro com inicializadores de coleção acontece porque o método retorna novas instâncias cada vez que você o ImmutableArray.Add chama. Como ImmutableArrays nunca muda, quando você adiciona um novo elemento, você obtém de volta um novo objeto ImmutableArray (que pode compartilhar armazenamento por motivos de desempenho com um ImmutableArray existente anteriormente). Porque b2 aponta para o primeiro ImmutableArray antes de chamar Add() cinco vezes, b2 é um ImmutableArray padrão. Comprimento de chamada nele também falha com um erro de desreferenciação nulo. A maneira correta de inicializar um ImmutableArray sem chamar Add manualmente é usar ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5})o .

Encontre tipos de nó de sintaxe relevantes para acionar o analisador

Para começar a construir o analisador, primeiro descubra que tipo de SyntaxNode você precisa procurar. Inicie o Visualizador de Sintaxe no menu Exibir>Outro Visualizador de Sintaxe do Windows>Roslyn.

Coloque o acento circunflexo do editor na linha que declara b1. Você verá que o Visualizador de sintaxe mostra que você está em um LocalDeclarationStatement nó da árvore de sintaxe. Este nó tem um , que por sua vez tem um , que por sua vez tem um , e finalmente há um VariableDeclarationEqualsValueClauseVariableDeclaratorObjectCreationExpression. À medida que você clica na árvore de nós do Visualizador de Sintaxe, a sintaxe na janela do editor é realçada para mostrar o código representado por esse nó. Os nomes dos subtipos SyntaxNode correspondem aos nomes usados na gramática C#.

Criar o projeto do analisador

No menu principal, escolha Arquivo>Novo>Projeto. Na caixa de diálogo Novo Projeto, em Projetos C# na barra de navegação esquerda, escolha Extensibilidade e, no painel direito, escolha o modelo de projeto Analisador com Correção de Código. Digite um nome e confirme a caixa de diálogo.

O modelo abre um arquivo DiagnosticAnalyzer.cs Escolha essa guia de buffer do editor. Esse arquivo tem uma classe de analisador (formada a partir do nome que você deu ao projeto) que deriva de (um tipo de DiagnosticAnalyzer API Roslyn). Sua nova classe tem uma DiagnosticAnalyzerAttribute declaração de que seu analisador é relevante para a linguagem C# para que o compilador descubra e carregue seu analisador.

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ImmutableArrayAnalyzer : DiagnosticAnalyzer
{}

Você pode implementar um analisador usando o Visual Basic que tem como alvo o código C# e vice-versa. É mais importante no DiagnosticAnalyzerAttribute escolher se o analisador tem como alvo um idioma ou ambos. Analisadores mais sofisticados que exigem modelagem detalhada da linguagem só podem ter como alvo um único idioma. Se o analisador, por exemplo, verificar apenas nomes de tipo ou nomes de membros públicos, talvez seja possível usar o modelo de linguagem comum que o Roslyn oferece no Visual Basic e no C#. Por exemplo, FxCop avisa que uma classe implementa ISerializable, mas a classe não tem o SerializableAttribute atributo é independente de linguagem e funciona para Visual Basic e código C#.

Inicializar o analisador

Role um pouco para baixo na classe para DiagnosticAnalyzer ver o Initialize método. O compilador chama esse método ao ativar um analisador. O método usa um AnalysisContext objeto que permite que o analisador obtenha informações de contexto e registre retornos de chamada para eventos para os tipos de código que você deseja analisar.

public override void Initialize(AnalysisContext context) {}

Abra uma nova linha nesse método e digite "context." para ver uma lista de conclusão do IntelliSense. Você pode ver na lista de conclusão há muitos Register... métodos para lidar com vários tipos de eventos. Por exemplo, o primeiro, , chama de volta ao seu código para um bloco, RegisterCodeBlockActionque geralmente é código entre chaves. O registro de um bloco também chama de volta ao seu código o inicializador de um campo, o valor dado a um atributo ou o valor de um parâmetro opcional.

Como outro exemplo, , chama de volta ao seu código no início de uma compilação, RegisterCompilationStartActiono que é útil quando você precisa coletar o estado em muitos locais. Você pode criar uma estrutura de dados, digamos, para coletar todos os símbolos usados e, cada vez que o analisador for chamado de volta para alguma sintaxe ou símbolo, você poderá salvar informações sobre cada local em sua estrutura de dados. Quando você é chamado de volta devido ao final da compilação, você pode analisar todos os locais salvos, por exemplo, para relatar quais símbolos o código usa de cada using instrução.

Usando o Visualizador de Sintaxe, você aprendeu que deseja ser chamado quando o compilador processa um ObjectCreationExpression. Use este código para configurar o retorno de chamada:

context.RegisterSyntaxNodeAction(c => AnalyzeObjectCreation(c),
                                 SyntaxKind.ObjectCreationExpression);

Você se registra para um nó de sintaxe e filtro apenas para nós de sintaxe de criação de objeto. Por convenção, os autores do analisador usam um lambda ao registrar ações, o que ajuda a manter os analisadores sem monitoração de estado. Você pode usar o recurso do Visual Studio Gerar a partir do uso para criar o AnalyzeObjectCreation método. Isso gera o tipo correto de parâmetro de contexto para você também.

Definir propriedades para usuários do analisador

Para que o analisador apareça na interface do usuário do Visual Studio apropriadamente, procure e modifique a seguinte linha de código para identificar o analisador:

internal const string Category = "Naming";

Alterar "Naming" para "API Guidance".

Em seguida, localize e abra o arquivo Resources.resx em seu projeto usando o Gerenciador de Soluções. Você pode colocar uma descrição para o seu analisador, título, etc. Você pode alterar o valor de todos eles para "Don't use ImmutableArray<T> constructor" por enquanto. Você pode colocar argumentos de formatação de cadeia de caracteres em sua cadeia de caracteres ({0}, , etc.) e, mais tarde, quando você chamar Diagnostic.Create(), {1}você pode fornecer uma params matriz de argumentos a serem passados.

Analisar uma expressão de criação de objeto

O AnalyzeObjectCreation método usa um tipo diferente de contexto fornecido pela estrutura do analisador de código. O Initialize método permite AnalysisContext que você registre retornos de chamada de ação para configurar seu analisador. O SyntaxNodeAnalysisContext, por exemplo, tem um CancellationToken que você pode passar por aí. Se um usuário começar a digitar no editor, o Roslyn cancelará a execução de analisadores para economizar trabalho e melhorar o desempenho. Como outro exemplo, esse contexto tem uma propriedade Node que retorna o nó de sintaxe de criação de objeto.

Obtenha o nó, que você pode assumir que é o tipo para o qual você filtrou a ação do nó de sintaxe:

var objectCreation = (ObjectCreationExpressionSyntax)context.Node;

Inicie o Visual Studio com o analisador pela primeira vez

Inicie o Visual Studio criando e executando seu analisador (pressione F5). Como o projeto de inicialização no Gerenciador de Soluções é o projeto VSIX, a execução do código cria seu código e um VSIX e, em seguida, inicia o Visual Studio com esse VSIX instalado. Quando você inicia o Visual Studio dessa maneira, ele é iniciado com uma seção de registro distinta para que seu uso principal do Visual Studio não seja afetado por suas instâncias de teste durante a criação de analisadores. Na primeira vez que você iniciar dessa maneira, o Visual Studio faz várias inicializações semelhantes a quando você iniciou o Visual Studio pela primeira vez depois de instalá-lo.

Crie um projeto de console e, em seguida, insira o código da matriz em seus aplicativos de console Método principal:

var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = {0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = {0}", b2.Length);

As linhas de código com ImmutableArray têm squiggles porque você precisa obter o pacote NuGet imutável e adicionar uma using instrução ao seu código. Pressione o botão direito do ponteiro no nó do projeto no Gerenciador de Soluções e escolha Gerenciar Pacotes NuGet. No gerenciador NuGet, digite "Imutável" na caixa de pesquisa e escolha o item System.Collections.Immutable (não escolha Microsoft.Bcl.Immutable) no painel esquerdo e pressione o botão Instalar no painel direito. A instalação do pacote adiciona uma referência às referências do projeto.

Você ainda vê rabiscos vermelhos em ImmutableArray, então coloque o cursor nesse identificador e pressione Ctrl+ (ponto) para abrir o menu de correção sugerido e optar por adicionar a instrução apropriada.using

Salvar tudo e fechar a segunda instância do Visual Studio por enquanto para colocá-lo em um estado limpo para continuar.

Conclua o analisador usando editar e continue

Na primeira instância do Visual Studio, defina um ponto de interrupção no início do método AnalyzeObjectCreation pressionando F9 com o cursor na primeira linha.

Inicie o analisador novamente com F5 e, na segunda instância do Visual Studio, reabra o aplicativo de console criado pela última vez.

Você retorna à primeira instância do Visual Studio no ponto de interrupção porque o compilador Roslyn viu uma expressão de criação de objeto e chamou seu analisador.

Obtenha o nó de criação de objeto. Passe sobre a linha que define a variável pressionando F10 e, na janela imediata, avalie a objectCreation expressão "objectCreation.ToString()". Você vê que o nó de sintaxe para o qual a variável aponta é o código "new ImmutableArray<int>()", exatamente o que você está procurando.

Obtenha o objeto ImmutableArray<T> Type. Você precisa verificar se o tipo que está sendo criado é ImmutableArray. Primeiro, você obtém o objeto que representa esse tipo. Você verifica os tipos usando o modelo semântico para garantir que tem exatamente o tipo certo e não compara a cadeia de caracteres do ToString(). Digite a seguinte linha de código no final da função:

var immutableArrayOfTType =
    context.SemanticModel
           .Compilation
           .GetTypeByMetadataName("System.Collections.Immutable.ImmutableArray`1");

Você designa tipos genéricos em metadados com backticks (') e o número de parâmetros genéricos. É por isso que você não vê "... ImmutableArray<T>" no nome dos metadados.

O modelo semântico tem muitas coisas úteis que permitem que você faça perguntas sobre símbolos, fluxo de dados, tempo de vida variável, etc. Roslyn separa nós de sintaxe do modelo semântico por várias razões de engenharia (desempenho, modelagem de código errôneo, etc.). Você deseja que o modelo de compilação procure informações contidas em referências para comparação precisa.

Você pode arrastar o ponteiro de execução amarelo no lado esquerdo da janela do editor. Arraste-o até a linha que define a variável e passe sobre a objectCreation nova linha de código usando F10. Se você passar o ponteiro do mouse sobre a variável immutableArrayOfType, verá que encontramos o tipo exato no modelo semântico.

Obtenha o tipo da expressão de criação de objeto. "Tipo" é usado de algumas maneiras neste artigo, mas isso significa que se você tiver a expressão "novo Foo", você precisa obter um modelo de Foo. Você precisa obter o tipo da expressão de criação de objeto para ver se é o tipo ImmutableArray<T> . Use o modelo semântico novamente para obter informações de símbolo para o símbolo de tipo (ImmutableArray) na expressão de criação de objeto. Digite a seguinte linha de código no final da função:

var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as INamedTypeSymbol;

Como o analisador precisa lidar com código incompleto ou incorreto nos buffers do editor (por exemplo, há uma instrução ausente using ), você deve verificar se symbolInfo é null. Você precisa obter um tipo nomeado (INamedTypeSymbol) do objeto de informações de símbolo para concluir a análise.

Compare os tipos. Como há um tipo genérico aberto de T que estamos procurando, e o tipo no código é um tipo genérico concreto, você consulta as informações do símbolo para saber a partir do qual o tipo é construído (um tipo genérico aberto) e compara esse resultado com immutableArrayOfTTypeo . Digite o seguinte no final do método:

if (symbolInfo != null &&
    symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
{}

Relate o diagnóstico. Relatar o diagnóstico é muito fácil. Use a regra criada para você no modelo de projeto, que é definido antes do método Initialize. Como essa situação no código é um erro, você pode alterar a linha que inicializou a regra para substituir DiagnosticSeverity.Warning (squiggle verde) por DiagnosticSeverity.Error (squiggle vermelho). O restante da regra é inicializado a partir dos recursos editados perto do início da explicação passo a passo. Você também precisa relatar o local do squiggle, que é o local da especificação de tipo da expressão de criação de objeto. Digite este código no if bloco:

context.ReportDiagnostic(Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));

Sua função deve ter esta aparência (talvez formatada de forma diferente):

private void AnalyzeObjectCreation(SyntaxNodeAnalysisContext context)
{
    var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
    var immutableArrayOfTType =
        context.SemanticModel
               .Compilation
               .GetTypeByMetadataName(
                   "System.Collections.Immutable.ImmutableArray`1");
    var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as
        INamedTypeSymbol;
    if (symbolInfo != null &&
        symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
    {
        context.ReportDiagnostic(
            Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));
    }
}

Remova o ponto de interrupção para que você possa ver o analisador funcionando (e pare de retornar à primeira instância do Visual Studio). Arraste o ponteiro de execução para o início do método e pressione F5 para continuar a execução. Quando você alternar de volta para a segunda instância do Visual Studio, o compilador começará a examinar o código novamente e chamará o analisador. Você pode ver um rabisco em ImmutableType<int>.

Adicionando uma "correção de código" para o problema de código

Antes de começar, feche a segunda instância do Visual Studio e pare a depuração na primeira instância do Visual Studio (onde você está desenvolvendo o analisador).

Adicionar uma nova classe. Use o menu de atalho (botão de ponteiro direito) no nó do projeto no Gerenciador de Soluções e escolha adicionar um novo item. Adicione uma classe chamada BuildCodeFixProvider. Essa classe precisa derivar de , e você precisará usar Ctrl+. (ponto) para invocar a correção de CodeFixProvidercódigo que adiciona a instrução correta. using Essa classe também precisa ser anotada com ExportCodeFixProvider atributo e você precisará adicionar uma using instrução para resolver o LanguageNames enum. Você deve ter um arquivo de classe com o seguinte código:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;

namespace ImmutableArrayAnalyzer
{
    [ExportCodeFixProvider(LanguageNames.CSharp)]
    class BuildCodeFixProvider : CodeFixProvider
    {}

Stub fora membros derivados. Agora, coloque o cursor do editor no identificador CodeFixProvider e pressione Ctrl+. (ponto) para resumir a implementação para esta classe base abstrata. Isso gera uma propriedade e um método para você.

Implemente a propriedade . Preencha o corpo do get imóvel com o FixableDiagnosticIds seguinte código:

return ImmutableArray.Create(ImmutableArrayAnalyzer.DiagnosticId);

O Roslyn reúne diagnósticos e correções combinando esses identificadores, que são apenas cadeias de caracteres. O modelo de projeto gerou uma ID de diagnóstico para você, e você está livre para alterá-la. O código na propriedade apenas retorna o ID da classe do analisador.

O método RegisterCodeFixAsync usa um contexto. O contexto é importante porque uma correção de código pode se aplicar a vários diagnósticos ou pode haver mais de um problema em uma linha de código. Se você digitar "contexto." no corpo do método, a lista de conclusão do IntelliSense mostrará alguns membros úteis. Há um membro CancellationToken que você pode verificar para ver se algo deseja cancelar a correção. Há um membro Document que tem muitos membros úteis e permite que você acesse os objetos de modelo de projeto e solução. Há um membro Span que é o início e o fim do local de código especificado quando você relatou o diagnóstico.

Faça com que o método seja assíncrono. A primeira coisa que você precisa fazer é corrigir a declaração de método gerado para ser um async método. A correção de código para extrair a implementação de uma classe abstrata não inclui a async palavra-chave mesmo que o método retorne um Taskarquivo .

Obtenha a raiz da árvore de sintaxe. Para modificar o código, você precisa produzir uma nova árvore de sintaxe com as alterações feitas pela correção de código. Você precisa do Document contexto para chamar GetSyntaxRootAsync. Esse é um método assíncrono porque há um trabalho desconhecido para obter a árvore de sintaxe, possivelmente incluindo obter o arquivo do disco, analisá-lo e criar o modelo de código Roslyn para ele. A interface do usuário do Visual Studio deve ser responsiva durante esse tempo, que usando async habilita. Substitua a linha de código no método com o seguinte:

var root = await context.Document
                        .GetSyntaxRootAsync(context.CancellationToken);

Encontre o nó com o problema. Você passa na extensão do contexto, mas o nó encontrado pode não ser o código que você precisa alterar. O diagnóstico relatado fornecia apenas a extensão para o identificador de tipo (onde o rabisco pertencia), mas você precisa substituir toda a expressão de criação de objeto, incluindo a new palavra-chave no início e os parênteses no final. Adicione o seguinte código ao seu método (e use Ctrl+. para adicionar uma using instrução para ObjectCreationExpressionSyntax):

var objectCreation = root.FindNode(context.Span)
                         .FirstAncestorOrSelf<ObjectCreationExpressionSyntax>();

Registre sua correção de código para a interface do usuário da lâmpada. Quando você registra sua correção de código, Roslyn se conecta à interface do usuário da lâmpada do Visual Studio automaticamente. Os usuários finais verão que podem usar Ctrl+ (ponto final) quando o analisador rabiscar um ImmutableArray<T> mau uso do construtor. Como seu provedor de correção de código só é executado quando há um problema, você pode assumir que tem a expressão de criação de objeto que estava procurando. No parâmetro context, você pode registrar a nova correção de código adicionando o seguinte código ao final do RegisterCodeFixAsync método:

context.RegisterCodeFix(
            CodeAction.Create("Use ImmutableArray<T>.Empty",
                              c => ChangeToImmutableArrayEmpty(objectCreation,
                                                               context.Document,
                                                               c)),
            context.Diagnostics[0]);

Você precisa colocar o cursor do editor no identificador e, em seguida, CodeActionusar Ctrl+ (ponto) para adicionar a instrução apropriada using para esse tipo.

Em seguida, coloque o ChangeToImmutableArrayEmpty cursor do editor no identificador e use Ctrl+. novamente para gerar esse stub de método para você.

Este último trecho de código adicionado registra a correção de código passando um CodeAction e a ID de diagnóstico para o tipo de problema encontrado. Neste exemplo, há apenas uma ID de diagnóstico para a qual esse código fornece correções, portanto, você pode apenas passar o primeiro elemento da matriz de IDs de diagnóstico. Ao criar o , você passa o CodeActiontexto que a interface do usuário da lâmpada deve usar como uma descrição da correção de código. Você também passa uma função que pega um CancellationToken e retorna um novo documento. O novo documento tem uma nova árvore de sintaxe que inclui o código corrigido que chama ImmutableArray.Empty. Esse trecho de código usa um lambda para que ele possa ser fechado sobre o nó objectCreation e o documento do contexto.

Construa a nova árvore de sintaxe. ChangeToImmutableArrayEmpty No método cujo stub você gerou anteriormente, insira a linha de código: ImmutableArray<int>.Empty;. Se você exibir a janela da ferramenta Visualizador de Sintaxe novamente, poderá ver que essa sintaxe é um nó SimpleMemberAccessExpression. É isso que esse método precisa construir e retornar em um novo documento.

A primeira alteração é ChangeToImmutableArrayEmpty adicionar async antes Task<Document> porque os geradores de código não podem assumir que o método deve ser assíncrono.

Preencha o corpo com o seguinte código para que seu método seja semelhante ao seguinte:

private async Task<Document> ChangeToImmutableArrayEmpty(
    ObjectCreationExpressionSyntax objectCreation, Document document,
    CancellationToken c)
{
    var generator = SyntaxGenerator.GetGenerator(document);
    var memberAccess =
        generator.MemberAccessExpression(objectCreation.Type, "Empty");
    var oldRoot = await document.GetSyntaxRootAsync(c);
    var newRoot = oldRoot.ReplaceNode(objectCreation, memberAccess);
    return document.WithSyntaxRoot(newRoot);
}

Você precisará colocar o cursor do editor no SyntaxGenerator identificador e usar Ctrl+ (ponto) para adicionar a instrução apropriada using para esse tipo.

Esse código usa SyntaxGenerator, que é um tipo útil para construir um novo código. Depois de obter um gerador para o documento que tem o código emitido, chamaMemberAccessExpression, ChangeToImmutableArrayEmpty passando o tipo que tem o membro que queremos acessar e passando o nome do membro como uma string.

Em seguida, o método busca a raiz do documento e, como isso pode envolver trabalho arbitrário no caso geral, o código aguarda essa chamada e passa o token de cancelamento. Os modelos de código Roslyn são imutáveis, como trabalhar com uma cadeia de caracteres .NET; Quando você atualiza a cadeia de caracteres, você obtém um novo objeto String em troca. Ao chamar ReplaceNodeo , você recebe de volta um novo nó raiz. A maior parte da árvore de sintaxe é compartilhada (porque é imutável), mas o objectCreation nó é substituído pelo memberAccess nó, bem como todos os nós pai até a raiz da árvore de sintaxe.

Experimente a correção do código

Agora você pode pressionar F5 para executar o analisador em uma segunda instância do Visual Studio. Abra o projeto de console que você usou antes. Agora você deve ver a lâmpada aparecer onde sua nova expressão de criação de objeto é para ImmutableArray<int>. Se você pressionar Ctrl+ (ponto), verá a correção do código e verá uma visualização da diferença de código gerada automaticamente na interface do usuário da lâmpada. Roslyn cria isso para você.

Dica Pro: Se você iniciar a segunda instância do Visual Studio e não vir a lâmpada com sua correção de código, talvez seja necessário limpar o cache de componentes do Visual Studio. Limpar o cache força o Visual Studio a reexaminar os componentes, portanto, o Visual Studio deve pegar seu componente mais recente. Primeiro, desligue a segunda instância do Visual Studio. Em seguida, no Windows Explorer, navegue até %LOCALAPPDATA%\Microsoft\VisualStudio\16.0Roslyn\. (O "16.0" muda de versão para versão com o Visual Studio.) Exclua o subdiretório ComponentModelCache.

Falar vídeo e concluir projeto de código

Você pode ver todo o código finalizado aqui. As subpastas DoNotUseImmutableArrayCollectionInitializer e DoNotUseImmutableArrayCtor têm um arquivo C# para localizar problemas e um arquivo C# que implementa as correções de código que aparecem na interface do usuário da lâmpada do Visual Studio. Observe que o código concluído tem um pouco mais de abstração para evitar buscar o objeto do tipo T> ImmutableArray<repetidamente. Ele usa ações registradas aninhadas para salvar o objeto de tipo em um contexto que está disponível sempre que as subações (analisar a criação do objeto e analisar inicializações de coleção) são executadas.