Sdílet prostřednictvím


Analyzátory Roslyn a knihovna rozpoznávající kód pro ImmutableArrays

Platforma kompilátoru .NET ("Roslyn") vám pomáhá vytvářet knihovny pracující s kódem. Knihovna přizpůsobená pro kód nabízí funkce, které můžete využívat, a nástroje, jako jsou analyzátory Roslyn, aby vám pomohli používat knihovnu nejlepším způsobem nebo se vyhnout chybám. V tomto tématu se dozvíte, jak vytvořit praktický analyzátor Roslyn k zachycení běžných chyb při použití balíčku NuGet System.Collections.Immutable. Příklad také ukazuje, jak poskytnout opravu kódu pro problém s kódem nalezený analyzátorem. Uživatelé vidí opravy kódu v rozhraní žárovky sady Visual Studio a mohou automaticky použít opravu kódu.

Začněte

K sestavení tohoto příkladu potřebujete následující:

  • Visual Studio 2015 (nikoli Express Edition) nebo novější verze Můžete použít bezplatnou visual studio Community Edition
  • Visual Studio SDK. Při instalaci sady Visual Studio můžete také zaškrtnout Nástroje rozšiřitelnosti sady Visual Studio v části Obvyklé nástroje, aby se současně nainstalovala sada SDK. Pokud jste již nainstalovali Visual Studio, můžete tuto sadu SDK nainstalovat také tak, že přejdete do hlavní nabídky Soubor>Nový>Project, zvolíte C# v levém navigačním podokně a pak zvolíte Rozšiřitelnost. Když zvolíte šablonu projektu "Nainstalovat nástroje rozšiřitelnosti sady Visual Studio", zobrazí se výzva ke stažení a instalaci sady SDK.
  • .NET Compiler Platform (Roslyn) SDK. Tuto sadu SDK můžete nainstalovat také tak, že přejdete do hlavní nabídky File>New>Project, zvolíte C# v levém navigačním podokně a pak zvolíte Rozšiřitelnost. Když zvolíte "Stáhnout šablonu projektu platformy .NET Compiler SDK", zobrazí se výzva ke stažení a instalaci této sady SDK. Tato sada SDK zahrnuje vizualizér syntaxe Roslyn. Tento užitečný nástroj vám pomůže zjistit, jaké typy modelů kódu byste měli hledat v analyzátoru. Infrastruktura analyzátoru volá kód pro konkrétní typy modelů kódu, takže se váš kód spustí jenom v případě potřeby a může se soustředit jenom na analýzu relevantního kódu.

Jaký je problém?

Představte si, že knihovnu poskytujete s podporou ImmutableArray (například System.Collections.Immutable.ImmutableArray<T>). Vývojáři jazyka C# mají spoustu zkušeností s poli .NET. Vzhledem k povaze ImmutableArrays a technik optimalizace používaných při implementaci intuice vývojářů v C# vedou uživatele vaší knihovny k psaní chybného kódu, jak je uvedeno níže. Kromě toho uživatelé nevidí své chyby až při běhu programu, což není uživatelský standard kvality, na který jsou zvyklí v sadě Visual Studio s .NET.

Uživatelé znají psaní kódu takto:

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

Vytváření prázdných polí k vyplnění následnými řádky kódu a použití syntaxe inicializátoru kolekce jsou pro vývojáře v jazyce C# známé. Zápis stejného kódu pro ImmutableArray však způsobí chybu při běhu programu.

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

První chyba je způsobená implementací ImmutableArray pomocí struktury pro zabalení podkladového úložiště dat. Struktury musí mít konstruktory bez parametrů, aby výrazy default(T) mohly vracet struktury se všemi nulovými nebo nulovými členy. Když kód přistupuje k b1.Length, dojde k běhové chybě nulové dereference, protože ve struktuře ImmutableArray neexistuje žádné základní pole úložiště. Správný způsob vytvoření prázdné immutableArray je ImmutableArray<int>.Empty.

K chybě při inicializátorech kolekce dochází, protože metoda ImmutableArray.Add vrací nové instance pokaždé, když ji zavoláte. Vzhledem k tomu, že ImmutableArrays se nikdy nezmění, při přidání nového prvku získáte zpět nový ImmutableArray objekt (který může sdílet úložiště z důvodů výkonu s dříve existující ImmutableArray). Protože b2 odkazuje na první ImmutableArray před voláním Add() pětkrát, b2 je výchozí ImmutableArray. Volání funkce Length také způsobí pád s chybou dereference null. Správný způsob inicializace ImmutableArray bez ručního volání Add je použít ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5}).

Vyhledání relevantních typů uzlů syntaxe pro aktivaci analyzátoru

Pokud chcete začít sestavovat analyzátor, nejprve zjistěte, jaký typ uzlu SyntaxNode potřebujete vyhledat. Z nabídky Zobrazit>Další okna>Roslyn Syntax Visualizerspusťte Syntax Visualizer.

Umístěte kurzor editoru na řádek, který deklaruje b1. Vizualizér syntaxe vám ukáže, že jste v LocalDeclarationStatement uzlu stromu syntaxe. Tento uzel má VariableDeclaration, který má zase VariableDeclarator, který má zase EqualsValueClause, a nakonec existuje ObjectCreationExpression. Když kliknete na strom Vizualizéru syntaxe uzlů, zvýrazní se syntaxe v okně editoru, aby se zobrazil kód reprezentovaný tímto uzlem. Názvy podtypů SyntaxNode odpovídají názvům použitým v gramatikě jazyka C#.

Vytvoření projektu analyzátoru

Z hlavního menu vyberte Soubor>Nový>Projekt. V dialogovém okně Nový projekt v části projektů C# na levém navigačním panelu zvolte Rozšiřitelnosta v pravém podokně zvolte Analyzer s šablonou projektu Oprava kódu. Zadejte název a potvrďte dialogové okno.

Šablona otevře soubor DiagnosticAnalyzer.cs. Zvolte kartu vyrovnávací paměti editoru. Tento soubor má třídu analyzátoru (vytvořenou z názvu projektu), která je odvozena od DiagnosticAnalyzer (typ rozhraní ROSlyn API). Vaše nová třída má DiagnosticAnalyzerAttribute deklarování analyzátoru je relevantní pro jazyk C#, aby kompilátor zjistil a načte analyzátor.

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

Analyzátor můžete implementovat pomocí jazyka Visual Basic, který cílí na kód jazyka C# a naopak. V DiagnosticAnalyzerAttribute je důležitější zvolit, jestli analyzátor cílí na jeden jazyk nebo obojí. Sofistikovanější analyzátory, které vyžadují podrobné modelování jazyka, můžou cílit pouze na jeden jazyk. Pokud váš analyzátor například kontroluje jen názvy typů nebo veřejné názvy členů, může být možné použít model společného jazyka, který Roslyn nabízí pro jazyky Visual Basic a C#. FxCop například varuje, že třída implementuje ISerializable, ale nemá atribut SerializableAttribute, který je jazykově nezávislý a funguje jak pro kód ve Visual Basicu, tak i v C#.

Inicializace analyzátoru

Posuňte se dolů ve třídě DiagnosticAnalyzer, abyste viděli metodu Initialize. Kompilátor volá tuto metodu při aktivaci analyzátoru. Metoda přebírá AnalysisContext objekt, který umožňuje analyzátoru získat informace o kontextu a zaregistrovat zpětná volání pro události pro druhy kódu, které chcete analyzovat.

public override void Initialize(AnalysisContext context) {}

Otevřete nový řádek v této metodě a zadejte "context". Zobrazí se seznam doplňování IntelliSense. V seznamu dokončení je k dispozici mnoho Register... metod pro zpracování různých druhů událostí. Například první, RegisterCodeBlockAction, volá zpět váš kód pro blok, což je obvykle kód obklopený složenými závorkami. Registrace bloku také volá zpět do kódu pro inicializátor pole, hodnotu zadanou atributem nebo hodnotu volitelného parametru.

Jako další příklad, RegisterCompilationStartActionvyvolává zpětné volání na váš kód na začátku kompilace, což je užitečné, když potřebujete shromáždit stav v mnoha různých částech programu. Můžete vytvořit datovou strukturu, například shromáždit všechny použité symboly a pokaždé, když se analyzátor volá zpět pro nějakou syntaxi nebo symbol, můžete uložit informace o jednotlivých umístěních v datové struktuře. Když jste znovu voláni kvůli ukončení kompilace, můžete analyzovat všechna umístění, která jste uložili, například nahlásit, jaké symboly kód používá z každého příkazu using.

Pomocí vizualizéru syntaxejste se dozvěděli, že chcete být zavoláni, když kompilátor zpracovává výraz ObjectCreationExpression. Tento kód použijete k nastavení zpětného volání:

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

Zaregistrujete uzel syntaxe a vyfiltrujete pouze uzly syntaxe vytváření objektů. Podle konvence používají autoři analyzátoru při registraci akcí lambda, což pomáhá udržovat analyzátory bezstavové. Funkci Visual Studio Generovat z využití můžete použít k vytvoření metody AnalyzeObjectCreation. Tím se také vygeneruje správný typ parametru kontextu.

Nastavení vlastností pro uživatele analyzátoru

Aby se váš analyzátor správně zobrazoval v uživatelském rozhraní sady Visual Studio, vyhledejte a upravte následující řádek kódu, aby identifikoval váš analyzátor:

internal const string Category = "Naming";

Změňte "Naming" na "API Guidance".

Dále v projektu vyhledejte a otevřete soubor Resources.resx pomocí průzkumníka řešení. Můžete zadat popis vašeho analyzátoru, název atd. Hodnoty všech těchto možností můžete prozatím změnit na "Don't use ImmutableArray<T> constructor". Argumenty formátování řetězců můžete vložit do řetězce ({0}, {1}atd.) a později při volání Diagnostic.Create()můžete zadat params pole argumentů, které se mají předat.

Analýza výrazu pro vytvoření objektu

Metoda AnalyzeObjectCreation přebírá jiný typ kontextu, který poskytuje architektura analyzátoru kódu. Metoda Initialize systému AnalysisContext vám umožňuje zaregistrovat akční zpětná volání pro nastavení vašeho analyzátoru. Například SyntaxNodeAnalysisContextCancellationToken, který můžete předávat. Pokud uživatel začne psát v editoru, Roslyn zruší spouštění analyzátorů, aby se ušetřila práce a zlepšil výkon. V jiném příkladu má tento kontext vlastnost pojmenovanou Node, která vrací uzel syntaxe vytváření objektu.

Získejte uzel, který je typem, u kterého předpokládáte, že jste provedli filtrování v akci uzlu syntaxe.

var objectCreation = (ObjectCreationExpressionSyntax)context.Node;

První spuštění sady Visual Studio s analyzátorem

Spusťte Visual Studio sestavením a spuštěním analyzátoru (stiskněte F5). Vzhledem k tomu, že spouštěcí projekt v Průzkumníku řešení je projekt VSIX, spuštění kódu sestaví kód a VSIX a potom spustí Visual Studio s nainstalovaným VSIX. Když tímto způsobem spustíte Visual Studio, spustí se s odlišným podregistrem registru, aby hlavní použití sady Visual Studio nebylo ovlivněno testovacími instancemi při sestavování analyzátorů. Při prvním spuštění tímto způsobem provede Visual Studio několik inicializací podobných při prvním spuštění sady Visual Studio po instalaci.

Vytvořte projekt konzoly a pak do metody Main konzolových aplikací zadejte kód pole:

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

Řádky kódu s ImmutableArray mají vlnovky, protože potřebujete získat neměnitelný balíček NuGet a do kódu přidat příkaz using. Stiskněte pravé tlačítko myši na uzlu projektu v Průzkumníku řešení a zvolte Spravovat balíčky NuGet . Ve správci NuGet zadejte do vyhledávacího pole "Immutable" a zvolte položku System.Collections.Immutable (nevybírejte Microsoft.Bcl.Immutable) v levém podokně a stiskněte tlačítko Nainstalovat v pravém podokně. Instalace balíčku přidá odkaz na odkazy na váš projekt.

Stále vidíte červené vlnovky pod ImmutableArray, takže umístěte kurzor do tohoto identifikátoru a stiskněte Ctrl+a (tečka), aby se zobrazila nabídka navrhované opravy, a vyberte přidání příslušného příkazu using.

Uložit vše a zavřít druhou instanci sady Visual Studio, abyste mohli pokračovat v čistém stavu.

Dokončete analyzátor s použitím funkce úpravy a pokračování.

V první instanci aplikace Visual Studio nastavte zarážku na začátku metody AnalyzeObjectCreation stisknutím klávesy F9 s kurzorem na první řádce.

Znovu spusťte analyzátor pomocí F5a v druhé instanci sady Visual Studio znovu otevřete konzolovou aplikaci, kterou jste vytvořili naposledy.

Na bodu přerušení se vrátíte do první instance sady Visual Studio, protože kompilátor Roslyn viděl vytváření objektů a volal váš analyzátor.

Získejte uzel pro vytvoření objektu. Přejděte přes čáru, která nastaví proměnnou objectCreation stisknutím klávesy F10a v příkazovém okně vyhodnotíte výraz "objectCreation.ToString()". Uvidíte, že uzel syntaxe, na který proměnná odkazuje, je kód "new ImmutableArray<int>()", jen to, co hledáte.

Získejte objekt typu ImmutableArray<T>. Musíte zkontrolovat, jestli typ, který se vytváří, je ImmutableArray. Nejprve získáte objekt, který představuje tento typ. Pomocí sémantického modelu zkontrolujete typy, abyste měli přesně správný typ a nerovnáte řetězec z ToString(). Na konec funkce zadejte následující řádek kódu:

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

Obecné typy v metadatech označíte pomocí zpětných znaménka (') a počtu obecných parametrů. Proto nevidíte "... ImmutableArray<T>" v názvu metadat.

Sémantický model má mnoho užitečných věcí, které vám umožňují klást otázky týkající se symbolů, toku dat, životnosti proměnných atd. Roslyn odděluje uzly syntaxe od sémantického modelu z různých technických důvodů (výkon, modelování chybného kódu atd.). Chcete, aby model kompilace vyhledá informace obsažené v odkazech pro přesné porovnání.

Žlutý ukazatel provádění můžete přetáhnout na levou stranu okna editoru. Přetáhněte ho nahoru na čáru, která nastaví proměnnou objectCreation, a přejděte přes nový řádek kódu pomocí F10. Pokud najedete myší na proměnnou immutableArrayOfType, zjistíte, že jsme v sémantickém modelu našli přesný typ.

Získejte typ výrazu pro vytvoření objektu. "Typ" se v tomto článku používá několika způsoby, ale to znamená, že pokud máte výraz "new Foo", potřebujete získat model Foo. Potřebujete získat typ výrazu pro vytvoření objektu, abyste zjistili, jestli se jedná o immutableArray<T> typ. Pomocí sémantického modelu znovu získejte informace o symbolu pro symbol typu (ImmutableArray) ve výrazu pro vytvoření objektu. Na konec funkce zadejte následující řádek kódu:

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

Protože váš analyzátor potřebuje zpracovat neúplný nebo nesprávný kód ve vyrovnávací paměti editoru (například chybí příkaz using), měli byste zkontrolovat, zda symbolInfo je null. K dokončení analýzy musíte získat pojmenovaný typ (INamedTypeSymbol) z objektu informací o symbolu.

Porovnejte typy. Vzhledem k tomu, že existuje otevřený obecný typ T, který hledáme, a typ v kódu je konkrétní obecný typ, dotazujete se na informace o symbolech, ze kterého je typ vytvořen (otevřený obecný typ) a porovnáte tento výsledek s immutableArrayOfTType. Na konci metody zadejte následující:

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

Nahlaste diagnostiku. Hlášení diagnostiky je poměrně snadné. Použijete pravidlo vytvořené pro vás v šabloně projektu, která je definována před inicializací metody. Vzhledem k tomu, že tato situace v kódu představuje chybu, můžete změnit řádek, který inicializuje Rule, aby se nahradilo DiagnosticSeverity.Warning (zelená vlnovka) za DiagnosticSeverity.Error (červená vlnovka). Zbývající část pravidla se inicializuje z prostředků, které jste upravili poblíž začátku návodu. Musíte také nahlásit umístění vlnovky, což je umístění typové specifikace výrazu vytvoření objektu. Do bloku if zadejte tento kód:

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

Vaše funkce by měla vypadat takto (možná jinak formátovaná):

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

Odeberte zarážku, abyste viděli, jak váš analyzátor funguje (a abyste se již nevraceli do první instance Visual Studio). Přetáhněte ukazatel provádění na začátek metody a stisknutím klávesy F5 pokračujte v provádění. Když přepnete zpět na druhou instanci sady Visual Studio, kompilátor znovu prozkoumá kód a zavolá do analyzátoru. Pod ImmutableType<int>můžete vidět vlnovku.

Přidání opravy kódu pro problém s kódem

Než začnete, zavřete druhou instanci sady Visual Studio a zastavte ladění v první instanci sady Visual Studio (kde vyvíjíte analyzátor).

Přidejte novou třídu. Použijte místní nabídku (tlačítko ukazatele pravým tlačítkem) v uzlu projektu v Průzkumníku řešení a zvolte přidání nové položky. Přidejte třídu s názvem BuildCodeFixProvider. Tato třída musí být odvozena z CodeFixProvidera budete muset použít Ctrl+. (tečka) k vyvolání opravy kódu, která přidá správný příkaz using. Tato třída musí být také opatřena poznámkami s atributem ExportCodeFixProvider a budete muset přidat příkaz using k vyřešení výčtu LanguageNames. Soubor třídy byste měli mít s následujícím kódem:

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

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

Zasuňte odvozené členy. Nyní umístěte kurzor editoru do identifikátoru CodeFixProvider a stiskněte Ctrl+. (tečka) k vygenerování implementace pro tuto abstraktní základní třídu. Tím se pro vás vygeneruje vlastnost a metoda.

Implementujte vlastnost. Do textu FixableDiagnosticIds vlastnosti get zadejte následující kód:

return ImmutableArray.Create(ImmutableArrayAnalyzer.DiagnosticId);

Roslyn spojuje diagnostiku a opravy tím, že tyto identifikátory odpovídají pouze řetězcům. Šablona projektu pro vás vygenerovala ID diagnostiky a můžete ji změnit. Kód ve vlastnosti pouze vrátí ID z třídy analyzátoru.

Metoda RegisterCodeFixAsync přebírá kontext. Kontext je důležitý, protože oprava kódu může platit pro více diagnostik nebo může na řádku kódu existovat více než jeden problém. Pokud do textu metody zadáte "context.", zobrazí se v seznamu dokončování IntelliSense několik užitečných členů. Existuje člen CancellationToken, který můžete zkontrolovat, jestli něco chce opravu zrušit. Existuje člen Document, který má spoustu užitečných funkcí a umožňuje přístup k objektům modelu projektu a řešení. Existuje člen Span, který je počátečním a koncovým bodem umístění kódu, jak je určeno při hlášení diagnostiky.

Nastavte metodu jako asynchronní. První věc, kterou musíte udělat, je opravit vygenerovanou deklaraci metody, aby byla async metoda. Oprava kódu pro vynechání implementace abstraktní třídy nezahrnuje klíčové slovo async, přestože metoda vrací Task.

Získejte kořen stromu syntaxe. Pokud chcete upravit kód, musíte vytvořit nový strom syntaxe se změnami, které provede oprava kódu. Pro volání GetSyntaxRootAsyncpotřebujete Document z kontextu. Jedná se o asynchronní metodu, protože pro získání stromu syntaxe existuje neznámá práce, například získání souboru z disku, jeho analýza a vytvoření modelu kódu Roslyn. Uživatelské rozhraní sady Visual Studio by mělo být během této doby responzivní, což umožňuje použití async. Řádek kódu v metodě nahraďte následujícím kódem:

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

Vyhledejte uzel s problémem. Předáváte rozsah kontextu, ale uzel, který zjistíte, nemusí být ten kód, který potřebujete změnit. Hlášená diagnostika poskytla rozsah pouze pro identifikátor typu (tam, kde je zobrazená vlnovka), ale musíte nahradit celý výraz pro vytvoření objektu, včetně klíčového slova new na začátku a závorek na konci. Do metody přidejte následující kód (a použijte Ctrl+. přidejte using příkaz pro ObjectCreationExpressionSyntax):

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

Zaregistrujte opravu kódu pro uživatelské rozhraní žárovky. Když zaregistrujete opravu kódu, Roslyn se automaticky připojí k uživatelskému rozhraní žárovky sady Visual Studio. Koncoví uživatelé poznají, že můžou používat Ctrl+. (tečka), když váš analyzátor podtrhne špatný ImmutableArray<T> konstruktor. Vzhledem k tomu, že se poskytovatel opravy kódu spustí jenom v případě problému, můžete předpokládat, že máte hledaný výraz pro vytvoření objektu. Z kontextového parametru můžete novou opravu kódu zaregistrovat přidáním následujícího kódu na konec RegisterCodeFixAsync metody:

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

Umístěte kurzor editoru do identifikátoru CodeActiona poté použijte Ctrl+. (tečka) k přidání příslušného příkazu using pro tento typ.

Pak umístěte kurzor editoru do identifikátoru ChangeToImmutableArrayEmpty a znovu použijte Ctrl+. k vytvoření tohoto základu metody.

Tento poslední fragment kódu, který jste přidali, zaregistruje opravu kódu předáním CodeAction a ID diagnostiky pro nalezený druh problému. V tomto příkladu existuje pouze jedno ID diagnostiky, pro které tento kód poskytuje opravy, takže stačí předat první prvek pole diagnostických ID. Při vytváření CodeActionpředáte text, který má uživatelské rozhraní žárovky použít jako popis opravy kódu. Předáváte také funkci, která přijímá argument CancellationToken a vrací nový dokument. Nový dokument má nový strom syntaxe, který zahrnuje váš opravený kód volající ImmutableArray.Empty. Tento fragment kódu používá lambda, aby se mohl zavřít přes uzel objectCreation a dokument kontextu.

Vytvořte nový strom syntaxe. V ChangeToImmutableArrayEmpty metodě, jejíž zástupný kód jste vygenerovali dříve, zadejte řádek kódu: ImmutableArray<int>.Empty;. Pokud znovu zobrazíte okno nástroje Vizualizér syntaxe, můžete vidět, že tato syntaxe je uzel SimpleMemberAccessExpression. To je to, co tato metoda potřebuje k vytvoření a vrácení v novém dokumentu.

První změnou ChangeToImmutableArrayEmpty je přidání async před Task<Document>, protože generátory kódu nemohou předpokládat, že metoda by měla být asynchronní.

Do textu zadejte následující kód, aby vaše metoda vypadala nějak takto:

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

Budete muset umístit kurzor editoru do identifikátoru SyntaxGenerator a použít Ctrl+. (tečka) pro přidání příslušného příkazu using pro tento typ.

Tento kód používá SyntaxGenerator, což je užitečný typ pro vytváření nového kódu. Po získání generátoru pro dokument, který má problém s kódem, ChangeToImmutableArrayEmpty volá MemberAccessExpression, předává typ, který má člena, k němuž chceme získat přístup, a předává název člena jako řetězec.

Dále metoda načte kořen dokumentu, a protože to může vyžadovat různorodou práci v obecném případě, kód čeká na toto volání a předá token zrušení. Modely kódu Roslyn jsou neměnné, podobně jako práce s řetězcem v .NET; při aktualizaci řetězce získáte nový objekt řetězce. Při volání ReplaceNodese vrátí nový kořenový uzel. Většina stromu syntaxe je sdílená (protože je neměnná), ale uzel objectCreation je nahrazen uzlem memberAccess a všechny nadřazené uzly až ke kořeni stromu syntaxe.

Vyzkoušejte opravu kódu.

Teď můžete stisknout F5 spustit analyzátor v druhé instanci sady Visual Studio. Otevřete projekt konzoly, který jste použili dříve. Nyní byste měli vidět, že žárovka se zobrazí tam, kde je nový výraz pro vytvoření objektu pro ImmutableArray<int>. Pokud stisknete Ctrl+. (období), zobrazí se oprava kódu a v uživatelském rozhraní žárovky se zobrazí automaticky vygenerovaný náhled rozdílu kódu. Roslyn to pro vás vytvoří.

Tip pro: Pokud spustíte druhou instanci sady Visual Studio a nevidíte žárovku s opravou kódu, možná budete muset vymazat mezipaměť součástí sady Visual Studio. Vymazání mezipaměti přinutí Visual Studio znovu prozkoumat komponenty, takže Visual Studio by pak mělo rozpoznat vaši nejnovější komponentu. Nejprve vypněte druhou instanci sady Visual Studio. Potom v Průzkumníka Windowspřejděte na %LOCALAPPDATA%\Microsoft\VisualStudio\16.0Roslyn\. (Číslo „16.0“ se ve verzi Visual Studio mění s každou verzí.) Odstraňte podadresář ComponentModelCache.

Diskuse o videu a dokončení programátorského projektu

Můžete vidět veškerý hotový kód zde. Podsložky DoNotUseImmutableArrayCollectionInitializer a DoNotUseImmutableArrayCtor mají každý soubor C# pro hledání problémů a soubor C#, který implementuje opravy kódu, které se zobrazí v uživatelském rozhraní žárovky sady Visual Studio. Všimněte si, že dokončený kód je o něco abstraktnější, aby se předešlo opakovanému načítání objektu typu ImmutableArray<T>. Používá vnořené registrované akce k uložení objektu typu v kontextu, který je k dispozici při každém spuštění dílčích akcí (analýza vytvoření objektu a analýza inicializace kolekce).