Dela via


Roslyn-analysverktyg och kodmedvetna bibliotek för ImmutableArrays

.NET Compiler Platform ("Roslyn") hjälper dig att skapa kodmedvetna bibliotek. Ett kodmedvetent bibliotek innehåller funktioner som du kan använda och verktyg (Roslyn-analysverktyg) för att hjälpa dig att använda biblioteket på bästa sätt eller för att undvika fel. Det här avsnittet visar hur du skapar en verklig Roslyn-analysator för att fånga vanliga fel när du använder System.Collections.Immutable NuGet-paketet. Exemplet visar också hur du tillhandahåller en kodkorrigering för ett kodproblem som hittas av analysatorn. Användare ser kodkorrigeringar i Visual Studio-gränssnittet för glödlampor och kan tillämpa en korrigering för koden automatiskt.

Sätta igång

Du behöver följande för att skapa det här exemplet:

  • Visual Studio 2015 (inte en Express Edition) eller en senare version. Du kan använda den kostnadsfria Visual Studio Community Edition
  • Visual Studio SDK. När du installerar Visual Studio kan du också kontrollera Visual Studio Extensibility Tools under Common Tools för att installera SDK:et samtidigt. Om du redan har installerat Visual Studio kan du också installera den här SDK:n genom att gå till huvudmenyn Arkiv>Nytt>Project, välja C# i det vänstra navigeringsfönstret och sedan välja Utökningsbarhet. När du väljer projektmallen "Install the Visual Studio Extensibility Tools" breadcrumb , uppmanas du att ladda ned och installera SDK: et.
  • .NET Compiler Platform ("Roslyn") SDK. Du kan också installera den här SDK:n genom att gå till huvudmenyn Arkiv>Nytt>Project, välja C# i det vänstra navigeringsfönstret och sedan välja Utökningsbarhet. När du väljer "Ladda ned .NET Compiler Platform SDK" breadcrumb-projektmall, uppmanas du att ladda ned och installera SDK. Det här SDK:et innehåller Roslyn Syntax Visualizer. Det här användbara verktyget hjälper dig att ta reda på vilka kodmodelltyper du bör leta efter i analysverktyget. Analyserarinfrastrukturen anropar din kod för specifika kodmodelltyper, så koden körs bara när det behövs och kan bara fokusera på att analysera relevant kod.

Vad är problemet?

Anta att du tillhandahåller ett bibliotek med stöd för ImmutableArray (till exempel System.Collections.Immutable.ImmutableArray<T>). C#-utvecklare har stor erfarenhet av .NET-matriser. Men på grund av den typ av ImmutableArrays och optimeringstekniker som används i implementeringen får C#-utvecklarens intuitioner användarna av biblioteket att skriva bruten kod, enligt beskrivningen nedan. Dessutom ser användarna inte sina fel förrän körningstiden, vilket inte är den kvalitetsupplevelse de är vana vid i Visual Studio med .NET.

Användarna är bekanta med att skriva kod som följande:

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

Att skapa tomma matriser för att fylla i med efterföljande kodrader och använda insamlingsinitieringssyntax är bekant för C#-utvecklare. Men att skriva samma kod för en ImmutableArray leder till krasch vid körning.

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

Det första felet beror på att ImmutableArray-implementeringen använder en struct för att omsluta den underliggande datalagringen. Structs måste ha parameterlösa konstruktorer så att default(T) uttryck kan returnera structs med alla noll- eller null-medlemmar. När koden kommer åt b1.Lengthuppstår ett null-avreferenslöpningstidfel eftersom det inte finns någon underliggande minnesmatris i ImmutableArray-strukturen. Rätt sätt att skapa en tom ImmutableArray är ImmutableArray<int>.Empty.

Felet med insamlingsinitiatorer inträffar eftersom metoden ImmutableArray.Add returnerar nya instanser varje gång du anropar den. Eftersom ImmutableArrays aldrig ändras, när du lägger till ett nytt element, får du tillbaka ett nytt ImmutableArray-objekt (som kan dela lagring av prestandaskäl med en tidigare befintlig ImmutableArray). Eftersom b2 pekar på den första ImmutableArray innan du anropar Add() fem gånger, är b2 en Standard ImmutableArray. Anropslängd på den kraschar också med ett null-dereference-fel. Det rätta sättet att initiera en ImmutableArray utan att manuellt anropa Lägg till är att använda ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5}).

Hitta relevanta syntaxnodtyper för att utlösa analysatorn

Börja skapa analysverktyget genom att först ta reda på vilken typ av SyntaxNode du behöver leta efter. Starta Syntax Visualizer från menyn Visa>Övriga fönster>Roslyn Syntax Visualizer.

Placera markören i redigeraren på raden som deklarerar b1. Syntaxvisualiseraren visar att du befinner dig i en LocalDeclarationStatement nod i syntaxträdet. Den här noden har en VariableDeclaration, som i sin tur har en VariableDeclarator, som i sin tur har en EqualsValueClause, och slutligen finns det en ObjectCreationExpression. När du klickar i syntaxvisualiserarträdet för noder markeras syntaxen i redigeringsfönstret för att visa koden som representeras av noden. Namnen på syntaxnodundertyperna matchar namnen som används i C#-grammatiken.

Skapa analysprojektet

På huvudmenyn väljer du Arkiv>Nytt>Projekt. I dialogrutan Nytt projekt, under C#-projekt på den vänstra navigeringsmenyn, väljer du Utökningsbarhet, och i den högra rutan väljer du projektmallen Analyzer med kodkorrigering. Ange ett namn och bekräfta dialogrutan.

Mallen öppnar en DiagnosticAnalyzer.cs fil. Välj fliken för redigerarens buffert. Den här filen har en analysator-klass (bildad från namnet du gav projektet) som härleds från DiagnosticAnalyzer (en Roslyn API-typ). Din nya klass har en DiagnosticAnalyzerAttribute som deklarerar att din analysator är relevant för C#-språket, så att kompilatorn kan identifiera och ladda in din analysator.

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

Du kan implementera en analysator med Visual Basic som riktar sig mot C#-kod och vice versa. Det är viktigare i DiagnosticAnalyzerAttribute att välja om analysatorn ska vara inriktad på ett språk eller båda. Mer avancerade analysverktyg som kräver detaljerad modellering av språket kan bara rikta in sig på ett enda språk. Om analysatorn till exempel bara kontrollerar typnamn eller offentliga medlemsnamn kan det vara möjligt att använda den gemensamma språkmodellen som Roslyn erbjuder i Visual Basic och C#. FxCop varnar till exempel för att en klass implementerar ISerializable, men klassen har inte attributet SerializableAttribute är språkoberoende och fungerar för både Visual Basic- och C#-kod.

Initiera analysatorn

Rulla ned lite i klassen DiagnosticAnalyzer för att se metoden Initialize. Kompilatorn anropar den här metoden när en analysator aktiveras. Metoden tar ett AnalysisContext objekt som gör att analysatorn kan komma åt kontextinformation och registrera återanrop för händelser för de typer av kod som du vill analysera.

public override void Initialize(AnalysisContext context) {}

Öppna en ny rad i den här metoden och skriv "context" för att se en IntelliSense-slutförandelista. Du kan se i slutförandelistan att det finns många Register... metoder för att hantera olika typer av händelser. Den första, RegisterCodeBlockAction, anropar till exempel tillbaka till koden för ett block, som vanligtvis är kod mellan klammerparenteser. Registrering för ett block anropar också tillbaka till din kod för initieringen av ett fält, värdet som ges till ett attribut eller värdet för en valfri parameter.

Som ett annat exempel, RegisterCompilationStartAction, kallar tillbaka din kod i början av en kompilering, vilket är användbart när du behöver samla in tillstånd över många ställen. Du kan till exempel skapa en datastruktur för att samla in alla symboler som används, och varje gång analyseraren anropas för viss syntax eller symbol kan du spara information om varje plats i datastrukturen. När du anropas tillbaka på grund av kompileringsslutet kan du analysera alla platser som du sparade, till exempel för att rapportera vilka symboler koden använder från varje using-instruktion.

Med hjälp av Syntax Visualizerhar du lärt dig att du vill anropas när kompilatorn bearbetar ett ObjectCreationExpression. Du använder den här koden för att konfigurera återanropet:

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

Du registrerar dig för en syntaxnod och filtrerar endast för syntaxnoder för skapande av objekt. Enligt konventionen använder analyserarförfattare en lambda när de registrerar åtgärder, vilket hjälper till att hålla analysverktyg tillståndslösa. I Visual Studio kan du använda funktionen Generera från användning för att skapa metoden AnalyzeObjectCreation. Detta genererar rätt typ av kontextparameter åt dig också.

Ange egenskaper för användare av analysatorn

Så att analysatorn visas i Visual Studio-användargränssnittet på rätt sätt, leta efter och ändra följande kodrad för att identifiera analysatorn:

internal const string Category = "Naming";

Ändra "Naming" till "API Guidance".

Leta sedan upp och öppna filen Resources.resx i projektet med hjälp av Solution Explorer. Du kan ange en beskrivning för analysatorn, rubriken osv. Du kan ändra värdet för alla dessa till "Don't use ImmutableArray<T> constructor" för tillfället. Du kan placera strängformateringsargument i strängen ({0}, {1}osv.) och senare när du anropar Diagnostic.Create()kan du ange en params matris med argument som ska skickas.

Analysera ett objektskapandeuttryck

Metoden AnalyzeObjectCreation tar en annan typ av kontext som tillhandahålls av kodanalysramverket. Med Initialize metodens AnalysisContext kan du registrera åtgärdsåteranrop för att konfigurera analysatorn. SyntaxNodeAnalysisContexthar till exempel en CancellationToken som du kan skicka runt. Om en användare börjar skriva i redigeraren avbryter Roslyn analysverktyg som körs för att spara arbete och förbättra prestanda. Som ett annat exempel har den här kontexten en Node-egenskap som returnerar syntaxnoden för skapande av objekt.

Hämta noden, som du kan anta är den typ som du filtrerade syntaxnodåtgärden för:

var objectCreation = (ObjectCreationExpressionSyntax)context.Node;

Starta Visual Studio med analysatorn första gången

Starta Visual Studio genom att skapa och köra analysatorn (tryck på F5). Eftersom startprojektet i Solution Explorer är VSIX-projektet, byggs din kod och en VSIX, och sedan startas Visual Studio med den VSIX installerad. När du startar Visual Studio på det här sättet startas det med en distinkt registerdatafil så att din huvudsakliga användning av Visual Studio inte påverkas av dina testinstanser när du skapar analysverktyg. Första gången du startar på det här sättet gör Visual Studio flera initieringar som liknar när du först startade Visual Studio när du har installerat det.

Skapa ett konsolprojekt och ange sedan matriskoden i huvudmetoden för konsolprogram:

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

Kodraderna med ImmutableArray har vågiga understrykningar eftersom du behöver hämta det oföränderliga NuGet-paketet samt lägga till en using-instruktion i din kod. Tryck på höger pekare på projektnoden i Solution Explorer och välj Hantera NuGet-paket. I NuGet-hanteraren skriver du "Oföränderlig" i sökrutan och väljer objektet System.Collections.Immutable (välj inte Microsoft.Bcl.Immutable) i det vänstra fönstret och tryck på knappen Installera i det högra fönstret. När du installerar paketet läggs en referens till dina projektreferenser.

Du ser fortfarande röda vågiga linjer under ImmutableArray, så placera markören i identifieraren och tryck på Ctrl+. (punkt) för att ta upp den föreslagna korrigeringsmenyn och välja att lägga till den lämpliga using-instruktionen.

Spara alla och Stäng den andra instansen av Visual Studio för nu så att du kan fortsätta i en ren miljö.

Slutför analysatorn med redigering och fortsätt

I den första instansen av Visual Studio anger du en brytpunkt i början av din AnalyzeObjectCreation-metod genom att trycka på F9 med markören på den första raden.

Starta analysatorn igen med F5och öppna konsolprogrammet som du skapade förra gången i den andra instansen av Visual Studio.

Du återgår till den första instansen av Visual Studio vid brytpunkten eftersom Roslyn-kompilatorn såg ett objektskapande uttryck och anropade din analysator.

Hämta noden för att skapa objektet. Stega över raden som anger variabeln objectCreation genom att trycka på F10, och i omedelbart fönster utvärdera uttrycket "objectCreation.ToString()". Du ser att syntaxnoden som variabeln pekar på är koden "new ImmutableArray<int>()", precis vad du letar efter.

Hämta Objektet ImmutableArray<T> Type. Du måste kontrollera om typen som skapas är ImmutableArray. Först får du objektet som representerar den här typen. Du kontrollerar typer med hjälp av semantikmodellen för att se till att du har exakt rätt typ och att du inte jämför strängen från ToString(). Ange följande kodrad i slutet av funktionen:

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

Du anger generiska typer i metadata med backticks (') och antalet generiska parametrar. Det är därför du inte ser "... ImmutableArray<T>" i metadatanamnet.

Den semantiska modellen har många användbara saker som gör att du kan ställa frågor om symboler, dataflöde, variabel livslängd osv. Roslyn separerar syntaxnoder från den semantiska modellen av olika tekniska skäl (prestanda, modellering av felaktig kod osv.). Du vill att kompileringsmodellen ska söka efter information som finns i referenser för korrekt jämförelse.

Du kan dra den gula körningspekaren på vänster sida av redigeringsfönstret. Dra upp den till den rad som anger variabeln objectCreation och gå över den nya kodraden med hjälp av F10. Om du håller muspekaren över variabeln immutableArrayOfTypeser du att vi hittade den exakta typen i semantikmodellen.

Hämta objektskapandeuttryckets typ. "Typ" används på några sätt i den här artikeln, men det innebär att om du har ett "nytt Foo"-uttryck måste du skaffa en modell av Foo. Du måste hämta typen av objektets skapandeuttryck för att kontrollera om det är av typen ImmutableArray<T>. Använd den semantiska modellen igen för att hämta symbolinformation för typsymbolen (ImmutableArray) i objektets skapandeuttryck. Ange följande kodrad i slutet av funktionen:

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

Eftersom analysatorn måste hantera ofullständig eller felaktig kod i redigeringsbuffertar (till exempel saknas en using-instruktion) bör du söka efter symbolInfo som null. Du måste hämta en namngiven typ (INamedTypeSymbol) från symbolinformationsobjektet för att slutföra analysen.

Jämför typerna. Eftersom det finns en öppen allmän typ av T som vi letar efter, och typen i koden är en konkret allmän typ, frågar du symbolinformationen för vilken typ som är konstruerad från (en öppen generisk typ) och jämför resultatet med immutableArrayOfTType. Ange följande i slutet av metoden:

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

Rapportera diagnostiken. Det är ganska enkelt att rapportera diagnostiken. Du använder regeln som skapats åt dig i projektmallen, som definieras innan metoden Initiera. Eftersom den här situationen i koden är ett fel kan du ändra raden som initierade regeln för att ersätta DiagnosticSeverity.Warning (grön squiggle) med DiagnosticSeverity.Error (röd squiggle). Resten av regeln initieras från de resurser som du redigerade i början av genomgången. Du måste också ange positionen för den vågiga linjen, vilket är platsen för typen inom objektskapandeuttrycket. Ange den här koden i if-blocket:

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

Funktionen bör se ut så här (kanske formaterad på ett annat sätt):

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

Ta bort brytpunkten så att du kan se att analysatorn fungerar (och sluta återgå till den första instansen av Visual Studio). Dra körningspekaren till början av metoden och tryck på F5 för att fortsätta körningen. När du växlar tillbaka till den andra instansen av Visual Studio börjar kompilatorn undersöka koden igen och den anropas till analysverktyget. Du kan se en krumelur under ImmutableType<int>.

Lägga till en "kodkorrigering" för kodproblemet

Innan du börjar stänger du den andra instansen av Visual Studio och slutar felsöka i den första instansen av Visual Studio (där du utvecklar analysatorn).

Lägg till en ny klass. Använd snabbmenyn (höger pekarknapp) på projektnoden i Solution Explorer och välj att lägga till ett nytt objekt. Lägg till en klass med namnet BuildCodeFixProvider. Den här klassen måste härledas från CodeFixProvideroch du måste använda Ctrl+. (period) för att anropa kodkorrigeringen som lägger till rätt using-instruktion. Den här klassen måste också annoteras med ExportCodeFixProvider-attributet, och du måste lägga till en using-instruktion för att lösa LanguageNames-uppräkningen. Du bör ha en klassfil med följande kod i:

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

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

Stub ut härledda medlemmar. Placera nu redigerarens markör i identifieraren CodeFixProvider och tryck på Ctrl+. (period) för att skriva ut implementeringen för den här abstrakta basklassen. Detta genererar en egenskap och en metod åt dig.

Implementera egenskapen. Fyll i FixableDiagnosticIds-egenskapens get brödtext med följande kod:

return ImmutableArray.Create(ImmutableArrayAnalyzer.DiagnosticId);

Roslyn samlar diagnostik och korrigeringar genom att matcha dessa identifierare, som bara är strängar. Projektmallen genererade ett diagnostik-ID åt dig och du kan ändra det. Koden i egenskapen returnerar bara ID:t från analyzer-klassen.

Metoden RegisterCodeFixAsync tar en kontext. Kontexten är viktig eftersom en kodkorrigering kan gälla för flera diagnostiker, eller så kan det finnas fler än ett problem på en kodrad. Om du skriver "context." i brödtexten i metoden, visar IntelliSense-kompletteringslistan några användbara medlemmar. Det finns en medlem i CancellationToken som du kan kontrollera för att se om någon vill avbryta åtgärden. Det finns en dokumentmedlem som har många användbara medlemmar och låter dig komma till projekt- och lösningsmodellobjekten. Det finns en Span-medlem som är början och slutet av den kodplats som angavs när du rapporterade diagnostiken.

Gör metoden asynkron. Det första du behöver göra är att åtgärda den genererade metoddeklarationen så att den är en async metod. Kodkorrigeringen för att stubba ut implementeringen av en abstrakt klass innehåller inte nyckelordet async även om metoden returnerar en Task.

Hämta roten för syntaxträdet. Om du vill ändra kod måste du skapa ett nytt syntaxträd med de ändringar som kodkorrigeringen gör. Du behöver Document från kontexten för att anropa GetSyntaxRootAsync. Det här är en asynkron metod eftersom det finns ett okänt arbete för att hämta syntaxträdet, inklusive att hämta filen från disken, parsa den och skapa Roslyn-kodmodellen för den. Visual Studio-användargränssnittet bör vara responsivt under den här tiden, vilket möjliggörs av async. Ersätt kodraden i metoden med följande:

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

Hitta noden med problemet. Du skickar kontextens intervall, men den nod som du hittar kanske inte är den kod som du måste ändra. Den rapporterade diagnostiken angav endast intervallet för typidentifieraren (där krumelursymbolen hörde hemma), men du måste ersätta hela objektskapande uttrycket, inklusive nyckelordet new i början och parenteserna i slutet. Lägg till följande kod i metoden (och använd Ctrl+. för att lägga till en using-instruktion för ObjectCreationExpressionSyntax):

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

Registrera din kodkorrigering för glödlampans användargränssnitt. När du registrerar din kodkorrigering ansluter Roslyn automatiskt till Visual Studio-glödlampans användargränssnitt. Slutanvändarna ser att de kan använda Ctrl+. (punkt) när analysatorn markerar en felaktig ImmutableArray<T> konstruktoranvändning. Eftersom kodkorrigeringsprovidern bara körs när det finns ett problem kan du anta att du har det objektskapande uttryck som du letade efter. Från kontextparametern kan du registrera den nya kodkorrigeringen genom att lägga till följande kod i slutet av RegisterCodeFixAsync-metoden:

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

Du måste placera redigerarens caret i identifieraren, CodeActionoch sedan använda Ctrl+. (period) för att lägga till lämplig using-instruktion för den här typen.

Placera sedan redigerarens caret på ChangeToImmutableArrayEmpty-identifieraren och använd Ctrl+. för att generera igen metodstubben åt dig.

Det senaste kodfragmentet som du lade till registrerar kodkorrigeringen genom att skicka en CodeAction och diagnostik-ID:t för den typ av problem som hittades. I det här exemplet finns det bara ett diagnostik-ID som den här koden innehåller korrigeringar för, så du kan bara skicka det första elementet i matrisen för diagnostik-ID:t. När du skapar CodeActionskickar du in texten som glödlampans användargränssnitt ska använda som en beskrivning av kodkorrigeringen. Du skickar också med en funktion som tar en CancellationToken och returnerar ett nytt Dokument. Det nya dokumentet har ett nytt syntaxträd som innehåller din korrigerade kod som anropar ImmutableArray.Empty. Det här kodfragmentet använder en lambda så att den kan omsluta noden objectCreation och kontextens dokument.

Konstruera det nya syntaxträdet. I metoden ChangeToImmutableArrayEmpty vars stub du genererade tidigare anger du kodraden: ImmutableArray<int>.Empty;. Om du visar Syntax Visualizer verktygsfönstret igen kan du se att den här syntaxen är en SimpleMemberAccessExpression-nod. Det är vad den här metoden behöver för att konstruera ett nytt dokument och återsända det.

Den första ändringen av ChangeToImmutableArrayEmpty är att lägga till async innan Task<Document> eftersom kodgeneratorerna inte kan anta att metoden ska vara asynkron.

Fyll i brödtexten med följande kod så att metoden ser ut ungefär så här:

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

Du måste placera redigerarens caret i SyntaxGenerator-identifieraren och använda Ctrl+. (period) för att lägga till lämplig using-instruktion för den här typen.

Den här koden använder SyntaxGenerator, vilket är en användbar typ för att konstruera ny kod. När du har hämtat en generator för dokumentet som har kodproblemet anropar ChangeToImmutableArrayEmptyMemberAccessExpression, skickar den typ som har den medlem som vi vill komma åt och skickar namnet på medlemmen som en sträng.

Sedan hämtar metoden dokumentets rot, och eftersom detta kan innebära godtyckligt arbete i det allmänna fallet väntar koden på det här anropet och skickar annulleringstoken. Roslyn-kodmodeller är oföränderliga, som att arbeta med en .NET-sträng. när du uppdaterar strängen får du ett nytt strängobjekt i gengäld. När du anropar ReplaceNodefår du tillbaka en ny rotnod. Det mesta av syntaxträdet delas (eftersom det är oföränderligt), men den objectCreation noden ersätts med den memberAccess noden samt alla överordnade noder upp till syntaxträdroten.

Prova din kodändring

Du kan nu trycka på F5- för att köra analysatorn i en andra instans av Visual Studio. Öppna konsolprojektet som du använde tidigare. Nu bör du se glödlampan visas där ditt nya objektskapandeuttryck är för ImmutableArray<int>. Om du trycker på Ctrl+. (punkt) visas kodkorrigeringen och du ser en automatiskt genererad kodskillnadsöversikt i glödlampegränssnittet. Roslyn skapar detta åt dig.

Pro-tips: Om du startar den andra instansen av Visual Studio och inte ser glödlampan med kodkorrigeringen kan du behöva rensa Visual Studio-komponentcachen. Om cacheminnet rensas måste Visual Studio undersöka komponenterna igen, så Visual Studio bör sedan hämta den senaste komponenten. Stäng först av den andra instansen av Visual Studio. I Utforskarennavigerar du sedan till %LOCALAPPDATA%\Microsoft\VisualStudio\16.0Roslyn\. ("16.0" ändras från version till version med Visual Studio.) Ta bort underkatalogen ComponentModelCache.

Samtal om video och avsluta kodprojekt

Du kan se all färdig kod här. Undermapparna DoNotUseImmutableArrayCollectionInitializer och DoNotUseImmutableArrayCtor var och en har en C#-fil för att hitta problem och en C#-fil som implementerar de kodkorrigeringar som visas i Visual Studio-glödlampans användargränssnitt. Observera att den färdiga koden har lite mer abstraktion för att undvika att hämta objektet ImmutableArray<T> typ om och om igen. Den använder kapslade registrerade åtgärder för att spara typobjektet i en kontext som är tillgänglig när underåtgärderna (analysera objektskapande och analysera insamlingsinitieringar) körs.