Partilhar 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 funcionalidades 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 detetar 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, também poderá instalar esse SDK acessando 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 "Install the Visual Studio Extensibility Tools", ele solicita que você baixe e instale o SDK.
  • SDK da plataforma de compilador .NET ("Roslyn"). Você também pode instalar este 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 "Baixar o modelo de projeto de trilha do .NET Compiler Platform SDK", ele solicita que você baixe e instale o SDK. Este SDK inclui o Roslyn Syntax Visualizer. Esta 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 do código relevante.

Qual é o problema?

Imagine que você fornece 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 a que 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);

Criar matrizes vazias para preencher com linhas subsequentes de código e usar a 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 um struct para encapsular o armazenamento de dados subjacente. Structs devem ter construtores sem parâmetros para que as expressões default(T) possam retornar structs com todos os membros zero ou nulos. Quando o código acessa b1.Length, há um erro de desreferência nula em tempo de execução porque não há nenhum array de armazenamento subjacente na struct ImmutableArray. A maneira correta de criar um ImmutableArray vazio é ImmutableArray<int>.Empty.

O erro com inicializadores de coleção acontece porque o ImmutableArray.Add método retorna novas instâncias cada vez que você o chama. Como ImmutableArrays nunca muda, quando você adiciona um novo elemento, recebe 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. Chamar Length nele também falha com um erro de desreferência nulo. A maneira correta de inicializar um ImmutableArray sem chamar Add manualmente é usar ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5}).

Encontre tipos de nós de sintaxe relevantes para disparar o seu analisador

Para começar a construir o analisador, primeiro descubra que tipo de SyntaxNode você precisa procurar. Inicie o Visualizador de Sintaxe no menu Ver>Outras Janelas>Visualizador de Sintaxe Roslyn.

Coloque o cursor do editor na linha que declara b1. Você verá que o Visualizador de Sintaxe mostra que você está em um nó LocalDeclarationStatement da árvore de sintaxe. Este nó tem um VariableDeclaration, que por sua vez tem um VariableDeclarator, que por sua vez tem um EqualsValueClause, e finalmente há um ObjectCreationExpression. À 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 . Insira um nome e confirme a caixa de diálogo.

O modelo abre um arquivo DiagnosticAnalyzer.cs . Escolha esta guia de buffer do editor. Este arquivo tem uma classe de analisador (formada a partir do nome que deu ao projeto) que deriva de DiagnosticAnalyzer (um tipo de 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 uma única linguagem. 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 em Visual Basic e C#. Por exemplo, o FxCop avisa que uma classe implementa ISerializable, mas a classe não possui o atributo SerializableAttribute. Esta advertência é independente da linguagem e funciona tanto para código Visual Basic quanto para código C#.

Inicializar o analisador

Desça um pouco na classe DiagnosticAnalyzer para ver o método Initialize. O compilador chama esse método ao ativar um analisador. O método usa um AnalysisContext objeto que permite ao analisador obter informações de contexto e registrar 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 neste método e digite "context." para ver uma lista de conclusão do IntelliSense. Você pode ver na lista de conclusão que existem muitos Register... métodos para lidar com vários tipos de eventos. Por exemplo, o primeiro, RegisterCodeBlockAction, chama de volta para o seu código para um bloco, que geralmente é código entre chaves encaracoladas. O registro de um bloco também chama de volta ao seu código para o inicializador de um campo, o valor dado a um atributo ou o valor de um parâmetro opcional.

Como outro exemplo, RegisterCompilationStartAction, chama de volta para seu código no início de uma compilação, o 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 é chamado de volta para alguma sintaxe ou símbolo, você pode salvar informações sobre cada local em sua estrutura de dados. Quando és chamado de volta devido ao término da compilação, podes analisar todos os locais que guardaste, por exemplo, para relatar quais símbolos o código usa de cada instrução using.

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);

Regista-se para um nó de sintaxe e filtra apenas nós de sintaxe de criação de objeto. Por convenção, os autores do analisador usam uma função lambda ao registrar ações, o que ajuda a manter os analisadores sem estado. Você pode usar o recurso do Visual Studio Generate From Usage 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 adequadamente, procure e modifique a seguinte linha de código para identificar o analisador:

internal const string Category = "Naming";

Altere "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 na sua string ({0}, {1}, etc.), e, mais tarde, quando chamar Diagnostic.Create(), pode fornecer uma matriz params 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 método InitializeAnalysisContext permite-lhe registar callbacks de ação para configurar o seu analisador. O SyntaxNodeAnalysisContext, por exemplo, tem um CancellationToken que pode ser transferido. Se um usuário começar a digitar no editor, o Roslyn cancelará a execução de analisadores para salvar o 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 ser o tipo para o qual você filtrou a ação do nó de sintaxe:

var objectCreation = (ObjectCreationExpressionSyntax)context.Node;

Inicie o Visual Studio com seu 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. A primeira vez que você inicia 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 de ponteiro direito no nó do projeto no Gerenciador de Soluções e escolha Gerenciar Pacotes NuGet. No gerenciador NuGet, digite "Immutable" 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 Install no painel direito. A instalação do pacote adiciona uma referência às referências do projeto.

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

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

Conclua o analisador utilizando a funcionalidade de editar e continuar

Na primeira instância do Visual Studio, defina um ponto de interrupção no início do seu AnalyzeObjectCreation método 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 da última vez.

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

Obtenha o nó de criação do objeto. Passe sobre a linha que define a objectCreation variável pressionando F10 e, na janela imediata , avalie a 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 de ToString(). Insira 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 fazer 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 errado, etc.). Você deseja que o modelo de compilação procure informações contidas em referências para uma comparação precisa.

Você pode arrastar o ponteiro de execução amarelo no lado esquerdo da janela do editor. Arraste-o para cima até a linha que define a objectCreation variável e passe sobre sua 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 tiveres a expressão "new Foo", precisas de obter um modelo de Foo. Você precisa obter o tipo da expressão de criação de objeto para ver se é do 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. Insira 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 em 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 do 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 que o tipo é construído (um tipo genérico aberto) e compara esse resultado com immutableArrayOfTType. Insira o seguinte no final do método:

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

Relatar o diagnóstico. Relatar o diagnóstico é muito fácil. Você usa a Regra criada para você no modelo de projeto, que é definido antes do método Initialize. Como esta situação no código é um erro, pode-se alterar a linha que inicializou Rule para substituir DiagnosticSeverity.Warning (sublinhado ondulado verde) por DiagnosticSeverity.Error (sublinhado ondulado 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 para o squiggle, que é o local da especificação de tipo da expressão de criação de objeto. Insira este código no bloco if.

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 seu 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ê voltar para a segunda instância do Visual Studio, o compilador começará a examinar o código novamente e chamará seu analisador. Você pode ver um squiggle 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).

Adicione 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. Esta classe precisa derivar de , e será necessário usar Ctrl. (ponto) para invocar a correção de código que adiciona a instrução correta . Essa classe também precisa ser anotada com o atributo ExportCodeFixProvider, e necessitará adicionar uma declaração using para resolver o enum LanguageNames. 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
    {}

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

Implemente a propriedade. Preencha o FixableDiagnosticIds corpo da get propriedade com o seguinte código:

return ImmutableArray.Create(ImmutableArrayAnalyzer.DiagnosticId);

Roslyn reúne diagnósticos e correções combinando esses identificadores, que são apenas strings. O modelo de projeto gerou uma ID de diagnóstico para você, e você é 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 ser aplicada a vários diagnósticos ou pode haver mais de um problema em uma linha de código. Se você digitar "context." 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 do modelo de projeto e solução. Há um membro Span que abrange desde o início até ao fim da localização especificada do código quando foi reportado 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 gerada para ser um async método. A correção de código para simular a implementação de uma classe abstrata não inclui a palavra-chave async, mesmo que o método retorne um Task.

Obtenha a raiz da árvore de sintaxe. Para modificar o código, você precisa produzir uma nova árvore de sintaxe com as alterações que a correção de código faz. Você precisa do Document proveniente do contexto para chamar GetSyntaxRootAsync. Este é um método assíncrono porque há 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 de utilizador do Visual Studio deve ser responsiva durante este tempo, o que é permitido pelo uso de async. Substitua a linha de código no método pelo 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ó que você encontra pode não ser o código que você precisa alterar. O diagnóstico relatado forneceu apenas a extensão para o identificador de tipo (onde o squiggle 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>();

Registe a sua correção de código para a UI da lâmpada. Quando tu registas a tua correção de código, o Roslyn liga-se automaticamente à interface de utilizador da lâmpada do Visual Studio. Os utilizadores finais verão que podem usar Ctrl+. (ponto) quando o analisador sublinhar um 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. A partir do 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 CodeAction, e, em seguida, usar Ctrl+. (ponto) para adicionar a using instrução apropriada para esse tipo.

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

Este último trecho de código que você adicionou 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, para que você possa apenas passar o primeiro elemento da matriz de IDs de diagnóstico. Ao criar o CodeAction, você passa o texto 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 usa um CancellationToken e retorna um novo documento. O novo documento tem uma nova árvore de sintaxe que inclui o seu código corrigido que chama ImmutableArray.Empty. Esse trecho de código usa uma lambda para que possa capturar 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 a ChangeToImmutableArrayEmpty é a de adicionar async antes de Task<Document>, porque os geradores de código não podem pressupor que o método deva ser assíncrono.

Preencha o corpo com o seguinte código para que o seu método fique parecido com o 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 este tipo.

Este 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 problema de código, ChangeToImmutableArrayEmpty chama MemberAccessExpression, passando o tipo que tem o membro que queremos aceder 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; Ao atualizar a cadeia de caracteres, você obtém um novo objeto String em retorno. Quando você chama ReplaceNodeo , você recebe de volta um novo nó raiz. A maior parte da árvore sintática é compartilhada (porque é imutável), mas o nó objectCreation é substituído pelo nó memberAccess, bem como todos os nós pai até a raiz da árvore sintática.

Experimente a correção do código

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

Dica profissional: Se você iniciar a segunda instância do Visual Studio e não vir a lâmpada com a correção de código, talvez seja necessário limpar o cache de componentes do Visual Studio. Limpar a cache força o Visual Studio a reexaminar os componentes; assim, o Visual Studio deverá detetar o 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.

Conversa em 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 ImmutableArray<T> repetidamente. Ele usa ações registradas aninhadas para salvar o tipo de objeto em um contexto que está disponível sempre que as subações (analisar a criação de objetos e analisar as inicializações de coleção) são executadas.