Delen via


Roslyn-analyzers en code-bewuste bibliotheek voor onveranderbare arrays

De .NET Compiler Platform ("Roslyn") helpt u bij het bouwen van codebewuste bibliotheken. Een codebewuste bibliotheek biedt functionaliteit die u kunt gebruiken en hulpprogramma's (Roslyn Analyzers) om u te helpen de bibliotheek op de beste manier te gebruiken of om fouten te voorkomen. In dit onderwerp wordt beschreven hoe u een echte Roslyn Analyzer bouwt om veelvoorkomende fouten te ondervangen bij het gebruik van het System.Collections.Immutable NuGet-pakket. In het voorbeeld ziet u ook hoe u een codeoplossing kunt opgeven voor een codeprobleem dat door de analyse is gevonden. Gebruikers zien codecorrecties in de gebruikersinterface van visual Studio-gloeilampen en kunnen automatisch een oplossing voor de code toepassen.

Aan de slag

U hebt het volgende nodig om dit voorbeeld te bouwen:

  • Visual Studio 2015 (geen Express Edition) of een latere versie. U kunt de gratis Visual Studio Community Edition gebruiken
  • Visual Studio SDK. U kunt tijdens het installeren van Visual Studio ook Visual Studio Extensibility Tools controleren onder Common Tools om de SDK tegelijkertijd te installeren. Als u Visual Studio al hebt geïnstalleerd, kunt u deze SDK ook installeren door naar het hoofdmenu Bestand>Nieuwe>Projectte gaan, C# te kiezen in het linkernavigatiedeelvenster en vervolgens Uitbreidbaarheidte kiezen. Wanneer u de breadcrumb-projectsjabloon 'Installeer de Visual Studio Extensibility Tools' kiest, wordt u gevraagd de SDK te downloaden en te installeren.
  • .NET Compiler Platform ('Roslyn')-SDK. U kunt deze SDK ook installeren door naar het hoofdmenu Bestand>Nieuw>Projectte gaan, C#- in het linkernavigatiedeelvenster te kiezen en vervolgens Uitbreidbaarheidte kiezen. Wanneer u de projecttemplate 'Download the .NET Compiler Platform SDK' kiest, wordt u gevraagd om de SDK te downloaden en te installeren. Deze SDK bevat de Roslyn Syntax Visualizer. Dit handige hulpprogramma helpt u erachter te komen naar welke typen codemodellen u moet zoeken in uw analyse. De analyse-infrastructuur roept uw code aan voor specifieke codemodeltypen, dus uw code wordt alleen uitgevoerd wanneer dat nodig is en kan zich alleen richten op het analyseren van relevante code.

Wat is het probleem?

Stel dat u een bibliotheek biedt met ondersteuning voor ImmutableArray (bijvoorbeeld System.Collections.Immutable.ImmutableArray<T>). C#-ontwikkelaars hebben veel ervaring met .NET-matrices. Vanwege de aard van onveranderbare arrays en optimalisatietechnieken die in de implementatie worden gebruikt, leiden de intuïties van C#-ontwikkelaars ertoe dat gebruikers van uw bibliotheek onjuiste code schrijven, zoals hieronder wordt uitgelegd. Bovendien zien gebruikers hun fouten pas na runtime, wat niet de kwaliteitservaring is waarmee ze worden gebruikt in Visual Studio met .NET.

Gebruikers zijn bekend met het schrijven van code als de volgende:

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

Het maken van lege arrays om aan te vullen met volgende regels code en het gebruik van verzameling-initialisatiesyntaxis zijn bekend bij C#-ontwikkelaars. Het schrijven van dezelfde code voor een ImmutableArray loopt echter vast tijdens runtime:

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

De eerste fout is te wijten aan immutableArray-implementaties met behulp van een struct voor het verpakken van de onderliggende gegevensopslag. Structs moeten parameterloze constructors hebben, zodat default(T)-uitdrukkingen structs kunnen teruggeven met alle leden die nul of null zijn. Wanneer de code toegang krijgt tot b1.Length, is er een null-dereferentiefout tijdens runtime omdat er geen onderliggende opslagarray in de struct ImmutableArray staat. De juiste manier om een lege onveranderbarearray te maken, is ImmutableArray<int>.Empty.

De fout met initialisatie van verzamelingen treedt op omdat de methode ImmutableArray.Add nieuwe exemplaren retourneert telkens wanneer u deze aanroept. Omdat Onveranderbarearrays nooit worden gewijzigd, krijgt u, wanneer u een nieuw element toevoegt, een nieuw ImmutableArray-object terug (dat opslag kan delen om prestatieredenen met een eerder bestaande OnveranderbareArray). Omdat b2 verwijst naar de eerste onveranderbarearray voordat Add() vijf keer wordt aangeroepen, is b2 een standaard onveranderbarearray. Bij het aanroepen van de lengtefunctie treedt er ook een crash op vanwege een null-dereferentiefout. De juiste manier om een onveranderbarearray te initialiseren zonder handmatig toevoegen aan te roepen, is door ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5})te gebruiken.

Relevante typen syntaxisknooppunten zoeken om uw analyse te activeren

Om te beginnen met het bouwen van de analyse, moet u eerst uitzoeken naar welk type SyntaxNode u moet zoeken. Start de Syntax Visualizer vanuit het menu Weergave>Andere Windows>Roslyn Syntax Visualizer.

Plaats de caret van de editor op de regel die b1declareert. In de Syntax Visualisator ziet u dat u zich in een LocalDeclarationStatement knooppunt van de syntaxisboom bevindt. Dit knooppunt heeft een VariableDeclaration, die op zijn beurt een VariableDeclaratorheeft, die op zijn beurt een EqualsValueClauseheeft, en ten slotte is er een ObjectCreationExpression. Wanneer u in de Syntax Visualizer-boom met knooppunten klikt, wordt de syntaxis in het editorvenster gemarkeerd om de code aan te geven die door dat knooppunt wordt vertegenwoordigd. De namen van de SyntaxNode-subtypen komen overeen met de namen die worden gebruikt in de C#-grammatica.

Het analyseproject maken

Kies in het hoofdmenu Bestand>Nieuw>Project. Kies in het dialoogvenster Nieuw project onder C# projecten in de linkernavigatiebalk voor Uitbreidbaarheiden kies in het rechterdeelvenster de projectsjabloon Analyzer met codecorrectie. Voer een naam in en bevestig het dialoogvenster.

Met de sjabloon wordt een DiagnosticAnalyzer.cs bestand geopend. Kies het buffertabblad van de editor. Dit bestand heeft een analyseklasse (gevormd door de naam die u het project hebt gegeven) die is afgeleid van DiagnosticAnalyzer (een Roslyn-API-type). Uw nieuwe klasse heeft een DiagnosticAnalyzerAttribute-declaratie die aangeeft dat uw analyzer relevant is voor de C#-taal, zodat de compiler uw analyzer ontdekt en laadt.

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

U kunt met behulp van Visual Basic een analyse implementeren die is gericht op C#-code, en vice versa. Het is belangrijker in de DiagnosticAnalyzerAttribute om te kiezen of uw analyse is gericht op één taal of beide. Meer geavanceerde analyses waarvoor gedetailleerde modellering van de taal is vereist, kunnen slechts één taal bereiken. Als uw analyse bijvoorbeeld alleen typenamen of openbare ledennamen controleert, is het mogelijk om het algemene taalmodel Roslyn te gebruiken in Visual Basic en C#. FxCop waarschuwt bijvoorbeeld dat een klasse ISerializableimplementeert, maar de klasse beschikt niet over het kenmerk SerializableAttribute taalonafhankelijk is en werkt voor zowel Visual Basic- als C#-code.

De analyse initialiseren

Schuif een beetje omlaag in de klasse DiagnosticAnalyzer om de methode Initialize te zien. De compiler roept deze methode aan bij het activeren van een analyse. De methode gebruikt een AnalysisContext-object waarmee uw analyse contextinformatie kan ophalen en callbacks kan registreren voor gebeurtenissen voor de soorten code die u wilt analyseren.

public override void Initialize(AnalysisContext context) {}

Open een nieuwe regel in deze methode en typ 'context'. Als u een IntelliSense-voltooiingslijst wilt zien. U kunt in de voltooiingslijst zien dat er veel Register... methoden zijn om verschillende soorten gebeurtenissen te verwerken. Ter illustratie, de eerste, RegisterCodeBlockAction, roept terug naar je code voor een blok, wat meestal code is tussen accolades. Registreren voor een blok roept ook terug naar uw code voor de initialisatie van een veld, de waarde die aan een kenmerk is gegeven of de waarde van een optionele parameter.

Als een ander voorbeeld, roept RegisterCompilationStartActionaan het begin van een compilatie uw code aan, wat nuttig is wanneer u de toestand op meerdere locaties moet verzamelen. U kunt bijvoorbeeld een gegevensstructuur maken om alle gebruikte symbolen te verzamelen en telkens wanneer uw analyse wordt aangeroepen voor een syntaxis of symbool, kunt u informatie over elke locatie in uw gegevensstructuur opslaan. Wanneer u wordt teruggeroepen vanwege het einde van de compilatie, kunt u alle locaties analyseren die u hebt opgeslagen, bijvoorbeeld om te rapporteren welke symbolen de code van elke using instructie gebruikt.

Met behulp van de syntaxis visualiseren, hebt u geleerd dat u wilt worden aangeroepen wanneer de compiler een ObjectCreationExpression verwerkt. U gebruikt deze code om de callback in te stellen:

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

U registreert zich voor een syntaxisknooppunt en filtert alleen op syntaxisknooppunten voor het maken van objecten. Volgens de conventie gebruiken analyseauteurs een lambda bij het registreren van acties, wat helpt om staatloze analyses te behouden. U kunt de Visual Studio-functie Generate From Usage gebruiken om de methode AnalyzeObjectCreation te maken. Hiermee wordt ook het juiste type contextparameter voor u gegenereerd.

Eigenschappen instellen voor gebruikers van uw analyse

Zoek en wijzig de volgende coderegel om uw analyse te identificeren, zodat uw analyse op de juiste wijze wordt weergegeven in de Visual Studio-gebruikersinterface:

internal const string Category = "Naming";

Wijzig "Naming" in "API Guidance".

Zoek en open vervolgens het bestand Resources.resx in uw project met behulp van de Solution Explorer-. U kunt een beschrijving invoeren voor uw analyse, titel, enzovoort. U kunt de waarde voor al deze waarden voorlopig wijzigen in "Don't use ImmutableArray<T> constructor". U kunt tekenreeksopmaakargumenten in de tekenreeks plaatsen ({0}, {1}, enzovoort) en later wanneer u Diagnostic.Create()aanroept, kunt u een params matrix met argumenten opgeven die moeten worden doorgegeven.

Een expressie voor het maken van objecten analyseren

De methode AnalyzeObjectCreation gebruikt een ander type context dat wordt geleverd door het code analyzer-framework. Met de Initialize-methode AnalysisContext kunt u actie-callbacks registreren om uw analyse op te zetten. De SyntaxNodeAnalysisContextheeft bijvoorbeeld een CancellationToken die u kunt doorgeven. Als een gebruiker begint te typen in de editor, annuleert Roslyn actieve analyses om werk op te slaan en de prestaties te verbeteren. In een ander voorbeeld heeft deze context een knooppunteigenschap die het syntaxisknooppunt voor het maken van objecten retourneert.

Haal het knooppunt op, waarvan u kunt uitgaan dat het het type is waarvoor u de syntaxis-knooppuntactie hebt gefilterd.

var objectCreation = (ObjectCreationExpressionSyntax)context.Node;

Start Visual Studio voor de eerste keer met uw analysetool.

Start Visual Studio door uw analyse te bouwen en uit te voeren (druk op F5). Omdat het opstartproject in de Solution Explorer het VSIX-project is, wordt uw code gecompileerd en een VSIX aangemaakt, waarna Visual Studio wordt gestart met die VSIX geïnstalleerd. Wanneer u Visual Studio op deze manier start, wordt het gestart met een afzonderlijke register hive, zodat uw belangrijkste gebruik van Visual Studio niet wordt beïnvloed door uw testexemplaren tijdens het bouwen van analyses. De eerste keer dat u op deze manier start, voert Visual Studio verschillende initialisaties uit die vergelijkbaar zijn met wanneer u Visual Studio voor het eerst hebt gestart na de installatie.

Maak een consoleproject en voer vervolgens de matrixcode in de hoofdmethode van uw consoletoepassingen in:

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

De coderegels met ImmutableArray hebben kronkels omdat u het onveranderlijke NuGet-pakket moet ophalen en een using-instructie aan uw code moet toevoegen. Druk op de rechteraanwijzer op het projectknooppunt in de Solution Explorer- en kies NuGet-pakketten beheren. Typ in NuGet-beheer 'Onveranderbaar' in het zoekvak en kies het item System.Collections.Immutable (kies niet Microsoft.Bcl.Immutable) in het linkerdeelvenster en druk op de knop Installeren in het rechterdeelvenster. Als u het pakket installeert, wordt er een verwijzing naar uw projectverwijzingen toegevoegd.

U ziet nog steeds rode krabbels onder ImmutableArray, dus plaats de caret in die identifier en druk op Ctrl+, (punt) om het menu met voorgestelde oplossingen weer te geven en de juiste using instructie toe te voegen.

Sla alles op en sluit het tweede exemplaar van Visual Studio voor nu om u in een schone staat te brengen om door te gaan.

De analyse voltooien met bewerken en doorgaan

Stel in het eerste exemplaar van Visual Studio een onderbrekingspunt in aan het begin van je AnalyzeObjectCreation-methode door op F9 te drukken met de caret op de eerste regel.

Start uw analyse opnieuw met F5-en open in het tweede exemplaar van Visual Studio de consoletoepassing die u de laatste keer hebt gemaakt.

U keert terug naar het eerste exemplaar van Visual Studio op het onderbrekingspunt omdat de Roslyn-compiler een objectcreatie-expressie zag en uw analyzer aanriep.

Haal het objectcreatieknooppunt op. Stap over de regel waarmee de variabele objectCreation wordt ingesteld door op F10-te drukken en in het venster Direct de expressie "objectCreation.ToString()"te evalueren. U ziet dat het syntaxisknooppunt waarnaar de variabele verwijst de code "new ImmutableArray<int>()"is, precies wat u zoekt.

Verkrijg ImmutableArray<T> Type-object. U moet controleren of het type dat wordt gemaakt ImmutableArray is. Eerst krijgt u het object dat dit type vertegenwoordigt. U controleert typen met behulp van het semantische model om ervoor te zorgen dat u precies het juiste type hebt en u de tekenreeks niet vergelijkt met ToString(). Voer de volgende regel code aan het einde van de functie in:

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

U wijst algemene typen in metagegevens aan met backticks (') en het aantal algemene parameters. Daarom zie je niet "... ImmutableArray<T>" in de naam van de metagegevens.

Het semantische model bevat veel nuttige dingen waarmee u vragen kunt stellen over symbolen, gegevensstromen, variabele levensduur, enzovoort. Roslyn scheidt syntaxisknooppunten van het semantische model om verschillende technische redenen (prestaties, modellering onjuiste code, enzovoort). U wilt dat het compilatiemodel informatie in verwijzingen opzoekt voor een nauwkeurige vergelijking.

U kunt de gele uitvoeringsaanwijzer aan de linkerkant van het editorvenster slepen. Sleep deze omhoog naar de regel die de objectCreation-variabele instelt en stap over je nieuwe coderegel heen met behulp van F10. Als u de muisaanwijzer boven de variabele immutableArrayOfTypebeweegt, ziet u dat we het exacte type in het semantische model hebben gevonden.

Haal het type van de objectaanmaakexpressie op. 'Type' wordt op een aantal manieren in dit artikel gebruikt, maar dit betekent dat als u een 'nieuwe Foo'-expressie hebt, u een model van Foo moet ophalen. U moet het type expressie voor het maken van objecten ophalen om te zien of dit het type ImmutableArray<T> is. Gebruik het semantische model opnieuw om symboolgegevens op te halen voor het typesymbool (ImmutableArray) in de expressie voor het maken van objecten. Voer de volgende regel code aan het einde van de functie in:

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

Omdat uw analyse onvolledige of onjuiste code in editorbuffers moet verwerken (er is bijvoorbeeld een ontbrekende using instructie), moet u controleren of symbolInfonullis. U moet een benoemd type (INamedTypeSymbol) ophalen uit het symboolinformatieobject om de analyse te voltooien.

Vergelijk de typen. Omdat er een open algemeen type T is dat we zoeken en het type in de code een concreet algemeen type is, voert u een query uit op de symboolgegevens voor wat het type is samengesteld (een open algemeen type) en vergelijkt u dat resultaat met immutableArrayOfTType. Voer het volgende in aan het einde van de methode:

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

Rapporteer de diagnostische gegevens. Het rapporteren van de diagnose is vrij eenvoudig. U gebruikt de regel die voor u is gemaakt in de projectsjabloon, die wordt gedefinieerd vóór de methode Initialiseren. Omdat deze situatie in de code een fout is, kunt u de regel aanpassen die "Rule" initialiseert, om DiagnosticSeverity.Warning (groene golvende lijn) te vervangen door DiagnosticSeverity.Error (rode golvende lijn). De rest van de regel wordt geïnitialiseerd op basis van de bronnen die u aan het begin van de handleiding hebt bewerkt. U moet ook de locatie voor de kronkel rapporteren. Dit is de locatie van de typespecificatie van de expressie voor het aanmaken van een object. Voer deze code in het if blok in:

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

Uw functie moet er als volgt uitzien (mogelijk anders opgemaakt):

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

Verwijder het onderbrekingspunt zodat u uw analyse kunt zien werken (en stop met terugkeren naar het eerste exemplaar van Visual Studio). Sleep de uitvoeringsaanwijzer naar het begin van de methode en druk op F5- om door te gaan met de uitvoering. Wanneer u terugschakelt naar het tweede exemplaar van Visual Studio, begint de compiler de code opnieuw te onderzoeken en wordt deze aangeroepen in uw analyse. U kunt een kronkel onder ImmutableType<int>zien.

Een codeoplossing toevoegen voor het codeprobleem

Voordat u begint, sluit u het tweede exemplaar van Visual Studio en stopt u de foutopsporing in het eerste exemplaar van Visual Studio (waar u de analyse ontwikkelt).

Voeg een nieuwe klasse toe. Gebruik het snelmenu (knop aanwijzer rechts) op het projectknooppunt in de Solution Explorer- en kies ervoor om een nieuw item toe te voegen. Voeg een klasse toe met de naam BuildCodeFixProvider. Deze klasse moet worden afgeleid van CodeFixProvideren u moet Ctrl-+gebruiken. (punt) om de code-oplossing op te roepen waarmee de juiste using verklaring wordt toegevoegd. Deze klasse moet ook worden geannoteerd met ExportCodeFixProvider attribuut en u moet een using instructie toevoegen om de LanguageNames enumeratie op te lossen. U moet een klassebestand met de volgende code erin hebben:

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

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

Afgeleide leden uitwerken. Plaats nu de caret van de editor in de identifier CodeFixProvider en druk op Ctrl+. (punt) om de implementatie voor deze abstracte basisklasse te schetsen. Hiermee wordt een eigenschap en een methode voor u gegenereerd.

Implementeer de eigenschap. Vul de get hoofdtekst van de FixableDiagnosticIds eigenschap in met de volgende code:

return ImmutableArray.Create(ImmutableArrayAnalyzer.DiagnosticId);

Roslyn brengt diagnostische gegevens en oplossingen samen door deze id's te koppelen, die alleen tekenreeksen zijn. De projectsjabloon heeft een diagnostische id voor u gegenereerd en u kunt deze wijzigen. De code in de eigenschap retourneert alleen de id van de analyseklasse.

De methode RegisterCodeFixAsync heeft een context. De context is belangrijk omdat een codeoplossing kan worden toegepast op meerdere diagnostische gegevens of omdat er meer dan één probleem op een coderegel kan zijn. Als u 'context' typt in de hoofdtekst van de methode, worden in de intelliSense-voltooiingslijst enkele nuttige leden weergegeven. Er is een CancellationToken-element dat u kunt controleren om te zien of er iets is dat de bewerking wil annuleren. Er is een documentlid met veel nuttige leden en kunt u toegang krijgen tot de project- en oplossingsmodelobjecten. Er is een Span-lid dat het begin en einde markeert van de codelocatie die is opgegeven toen u de diagnostische melding rapporteerde.

De methode asynchroon maken. Het eerste wat u moet doen, is het herstellen van de gegenereerde methodedeclaratie als een async methode. De codeoplossing voor het stubberen van de implementatie van een abstracte klasse bevat niet het async trefwoord, ook al retourneert de methode een Task.

Haal de wortel van de syntaxisboom op. Als u code wilt wijzigen, moet u een nieuwe syntaxisstructuur maken met de wijzigingen die uw code-fix aanbrengt. U hebt de Document uit de context nodig om GetSyntaxRootAsyncaan te roepen. Dit is een asynchrone methode omdat er onbekend werk is om de syntaxisstructuur op te halen, waaronder het ophalen van het bestand van de schijf, het parseren ervan en het bouwen van het Roslyn-codemodel. De Visual Studio-gebruikersinterface moet gedurende deze tijd responsief zijn, waardoor het gebruik van async is ingeschakeld. Vervang de coderegel in de methode door het volgende:

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

Zoek het knooppunt met het probleem. U geeft het bereik van de context door, maar het knooppunt dat u vindt, is mogelijk niet de code die u moet wijzigen. De gerapporteerde diagnostische gegevens bevatten alleen de periode voor de type-id (waar de kronkel behoort), maar u moet de volledige expressie voor het maken van objecten vervangen, inclusief het new trefwoord aan het begin en de haakjes aan het einde. Voeg de volgende code toe aan uw methode (en gebruik Ctrl+. om een using-instructie toe te voegen voor ObjectCreationExpressionSyntax):

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

Registreer uw codefix voor de gebruikersinterface van de gloeilamp. Wanneer u uw codefix registreert, sluit Roslyn automatisch aan op de gebruikersinterface van de Visual Studio-gloeilamp. Eindgebruikers zullen zien dat ze Ctrl+kunnen gebruiken. (punt) wanneer uw analyzer een verkeerd gebruik van de ImmutableArray<T> constructor markeert. Omdat uw codeoplossingsprovider alleen wordt uitgevoerd wanneer er een probleem is, kunt u ervan uitgaan dat u de expressie voor het maken van objecten hebt die u zocht. Vanuit de contextparameter kunt u de nieuwe codefix registreren door de volgende code toe te voegen aan het einde van RegisterCodeFixAsync methode:

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

U moet de caret van de editor in de id plaatsen, CodeActionen vervolgens Ctrl-+gebruiken. (punt) om de juiste using instructie toe te voegen voor dit type.

Plaats vervolgens de caret van de editor in de ChangeToImmutableArrayEmpty-id en gebruik opnieuw Ctrl-+. om de methodestub voor u te genereren.

Dit laatste codefragment dat u hebt toegevoegd, registreert de codeoplossing door een CodeAction en de diagnostische id door te geven voor het soort probleem dat is gevonden. In dit voorbeeld is er slechts één diagnostische id waarvoor deze code oplossingen biedt, zodat u alleen het eerste element van de matrix met diagnostische id's kunt doorgeven. Wanneer u de CodeActionmaakt, geeft u de tekst door die de gebruikersinterface van de gloeilamp moet gebruiken als een beschrijving van de codeoplossing. U geeft ook een functie door die een CancellationToken gebruikt en een nieuw document retourneert. Het nieuwe document heeft een nieuwe syntaxisstructuur met uw gepatchte code die ImmutableArray.Emptyaanroept. Dit codefragment maakt gebruik van een lambda, zodat het het objectCreation-knooppunt en de context's Document kan vastleggen.

Maak de nieuwe syntaxisstructuur. Voer in de methode ChangeToImmutableArrayEmpty, waarvan u eerder de stub hebt gegenereerd, de regel code in: ImmutableArray<int>.Empty;. Als u het venster Syntaxisvisualisator hulpprogramma weer bekijkt, ziet u dat deze syntaxis een SimpleMemberAccessExpression-knooppunt is. Dat is wat deze methode nodig heeft om een nieuw document te maken en te retourneren.

De eerste wijziging in ChangeToImmutableArrayEmpty is het toevoegen van async voordat Task<Document> omdat de codegeneratoren niet kunnen aannemen dat de methode asynchroon moet zijn.

Vul de hoofdtekst in met de volgende code, zodat uw methode er ongeveer als volgt uitziet:

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

U moet de caret van de editor in de SyntaxGenerator-id plaatsen en Ctrl-+gebruiken. (punt) om de juiste using instructie toe te voegen voor dit type.

Deze code maakt gebruik van SyntaxGenerator, wat een nuttig type is voor het maken van nieuwe code. Na het verkrijgen van een generator voor het document met het codeprobleem, roept ChangeToImmutableArrayEmptyMemberAccessExpressionaan, waarbij het type wordt doorgegeven waarvan het lid geopend moet worden en geeft de naam van het lid als een tekenreeks door.

Vervolgens haalt de methode de root van het document op, en omdat dit arbitrair werk kan omvatten in het algemene geval, wacht de code op deze aanroep en geeft het annuleringstoken door. Roslyn-codemodellen zijn onveranderbaar, zoals werken met een .NET-tekenreeks; wanneer u de tekenreeks bijwerkt, krijgt u een nieuw tekenreeksobject als resultaat. Wanneer u ReplaceNodeaanroept, krijgt u een nieuw hoofdknooppunt terug. De meeste syntaxisstructuur wordt gedeeld (omdat deze onveranderbaar is), maar het objectCreation knooppunt wordt vervangen door het memberAccess-knooppunt, evenals alle bovenliggende knooppunten tot aan de hoofdmap van de syntaxisstructuur.

Probeer uw codefix

U kunt nu op F5 drukken om uw analyse uit te voeren in een tweede exemplaar van Visual Studio. Open het consoleproject dat u eerder hebt gebruikt. Nu zou u de gloeilamp moeten zien verschijnen op de plek waar uw nieuwe expressie voor het maken van objecten voor ImmutableArray<int>staat. Als u op Ctrl-+drukt. (punt), ziet u de codeoplossing en ziet u een automatisch gegenereerd codeverschilvoorbeeld in de gebruikersinterface van de gloeilamp. Roslyn creëert dit voor jou.

Pro Tip: Als u het tweede exemplaar van Visual Studio start en u de gloeilamp niet ziet met de codeoplossing, moet u mogelijk de cache van het Visual Studio-onderdeel wissen. Als u de cache wist, moet Visual Studio de componenten opnieuw onderzoeken, zodat Visual Studio uw nieuwste component kan oppikken. Sluit eerst het tweede exemplaar van Visual Studio af. Navigeer vervolgens in Windows Verkennernaar %LOCALAPPDATA%\Microsoft\VisualStudio\16.0Roslyn\. (De versie 16.0 wordt gewijzigd van versie in versie met Visual Studio.) Verwijder de submap ComponentModelCache.

Videogesprek voeren en codeproject voltooien

Hier ziet u alle voltooide code . De submappen DoNotUseImmutableArrayCollectionInitializer en DoNotUseImmutableArrayCtor elk een C#-bestand hebben voor het vinden van problemen en een C#-bestand waarmee de codeoplossingen worden geïmplementeerd die worden weergegeven in de gebruikersinterface van de Visual Studio-gloeilamp. Opmerking: de voltooide code heeft iets meer abstractie om te voorkomen dat het immutableArray-<T-> typeobject telkens opnieuw wordt opgehaald. Er worden geneste geregistreerde acties gebruikt om het type-object op te slaan in een context die beschikbaar is wanneer de subacties (het analyseren van objectcreatie en verzamelingsinitialisaties) worden uitgevoerd.