Freigeben über


Roslyn Analyzers und codefähige Bibliothek für ImmutableArrays

Die .NET-Compilerplattform ("Roslyn") hilft Ihnen beim Erstellen codefähiger Bibliotheken. Eine codefähige Bibliothek bietet Funktionen, die Sie verwenden und toolieren können (Roslyn Analyzer), um Die Bibliothek am besten zu verwenden oder Fehler zu vermeiden. In diesem Thema erfahren Sie, wie Sie einen echten Roslyn-Analyzer erstellen, um häufige Fehler bei Verwendung des Pakets "System.Collections.Immutable NuGet" zu erfassen. Im Beispiel wird auch veranschaulicht, wie Sie eine Codekorrektur für ein Codeproblem bereitstellen, das vom Analyzer gefunden wurde. Benutzern werden Codekorrekturen in der Visual Studio-Glühbirne-Benutzeroberfläche angezeigt und können automatisch einen Fix für den Code anwenden.

Erste Schritte

Zum Erstellen dieses Beispiels benötigen Sie Folgendes:

  • Visual Studio 2015 (keine Express Edition) oder eine höhere Version. Sie können die kostenlose Visual Studio Community Edition verwenden.
  • Visual Studio SDK. Sie können auch bei der Installation von Visual Studio die Visual Studio-Erweiterbarkeitstools unter "Allgemeine Tools" überprüfen, um das SDK gleichzeitig zu installieren. Wenn Sie Visual Studio bereits installiert haben, können Sie dieses SDK auch installieren, indem Sie zum Menü Standard Menü "Neues>Projekt">wechseln, im linken Navigationsbereich C# auswählen und dann "Erweiterbarkeit" auswählen. Wenn Sie die Breadcrumb-Projektvorlage "Visual Studio Extensibility Tools installieren" auswählen, werden Sie aufgefordert, das SDK herunterzuladen und zu installieren.
  • .NET Compiler Platform ("Roslyn") SDK. Sie können dieses SDK auch installieren, indem Sie zum Menü Standard Menü "Neues>Projekt">wechseln, C# im linken Navigationsbereich auswählen und dann "Erweiterbarkeit" auswählen. Wenn Sie die Breadcrumb-Projektvorlage ".NET Compiler Platform SDK herunterladen" auswählen, werden Sie aufgefordert, das SDK herunterzuladen und zu installieren. Dieses SDK enthält die Roslyn-Syntaxschnellansicht. Dieses nützliche Tool hilft Ihnen, herauszufinden, nach welchen Codemodelltypen Sie in Der Analyse suchen sollten. Die Analyseinfrastruktur ruft Ihren Code für bestimmte Codemodelltypen auf, sodass Ihr Code nur bei Bedarf ausgeführt wird und sich nur auf die Analyse relevanter Code konzentrieren kann.

Wo liegt das Problem?

Stellen Sie sich vor, System.Collections.Immutable.ImmutableArray<T>Sie stellen eine Bibliothek mit ImmutableArray (z. B. ) Unterstützung bereit. C#-Entwickler verfügen über viele Erfahrungen mit .NET-Arrays. Aufgrund der Art von ImmutableArrays und Optimierungstechniken, die in der Implementierung verwendet werden, führen C#-Entwickler-Intuitionen jedoch dazu, dass Benutzer Ihrer Bibliothek fehlerhaften Code schreiben, wie unten beschrieben. Darüber hinaus sehen Benutzer ihre Fehler erst zur Laufzeit, was nicht die Qualität ist, die sie in Visual Studio mit .NET verwenden.

Benutzer sind mit dem Schreiben von Code wie den folgenden vertraut:

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

Das Erstellen leerer Arrays zum Ausfüllen mit nachfolgenden Codezeilen und die Verwendung der Sammlungsinitialisierungssyntax sind C#-Entwicklern vertraut. Das Schreiben desselben Codes für ein ImmutableArray stürzt jedoch zur Laufzeit ab:

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

Der erste Fehler liegt daran, dass die ImmutableArray-Implementierung eine Struktur verwendet, um den zugrunde liegenden Datenspeicher umzuschließen. Structs müssen parameterlose Konstruktoren aufweisen, default(T) damit Ausdrücke Strukturs mit allen Null- oder NULL-Elementen zurückgeben können. Wenn der Code zugreift b1.Length, gibt es einen Laufzeit-Null-Ableitungsfehler, da in der ImmutableArray-Struktur kein zugrunde liegendes Speicherarray vorhanden ist. Die richtige Methode zum Erstellen eines leeren ImmutableArray ist ImmutableArray<int>.Empty.

Der Fehler mit Sammlungsinitialisierern tritt auf, da die ImmutableArray.Add Methode bei jedem Aufruf neue Instanzen zurückgibt. Da Sich ImmutableArrays nie ändern, wenn Sie ein neues Element hinzufügen, erhalten Sie ein neues ImmutableArray-Objekt (das aus Leistungsgründen speicherfähig sein kann, wenn Sie ein vorhandenes ImmutableArray-Objekt verwenden). Da b2 auf das erste ImmutableArray verweist, bevor fünf Mal aufgerufen Add() wird, b2 handelt es sich um ein Standardmäßiges ImmutableArray. Das Aufrufen der Länge stürzt auch mit einem Null-Ableitungsfehler ab. Die richtige Methode zum Initialisieren eines ImmutableArray ohne manuelles Aufrufen von "Add" ist die Verwendung ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5}).

Suchen relevanter Syntaxknotentypen zum Auslösen des Analyzers

Um mit der Erstellung des Analyzers zu beginnen, müssen Sie zunächst herausfinden, nach welchem SyntaxNode-Typ Sie suchen müssen. Starten Sie die Syntaxschnellansicht über das Menü ">Andere Windows>Roslyn-Syntaxschnellansicht anzeigen".

Platzieren Sie das Caret des Editors in der Zeile, die deklariert wird b1. Die Syntaxschnellansicht wird angezeigt, dass Sie sich in einem LocalDeclarationStatement Knoten der Syntaxstruktur befinden. Dieser Knoten hat einen VariableDeclaration, der wiederum eine VariableDeclarator, die wiederum einen EqualsValueClausehat , und schließlich gibt es eine ObjectCreationExpression. Während Sie auf die Syntaxschnellansichtsstruktur von Knoten klicken, wird in der Syntax im Editorfenster der Code hervorgehoben, der durch diesen Knoten dargestellt wird. Die Namen der SyntaxNode-Untertypen stimmen mit den namen überein, die in der C#-Grammatik verwendet werden.

Erstellen des Analyseprojekts

Wählen Sie im Menü Standard dateineues >>Projekt aus. Wählen Sie im Dialogfeld "Neues Projekt" unter "C#-Projekte" in der linken Navigationsleiste die Option "Erweiterbarkeit" aus, und wählen Sie im rechten Bereich die Projektvorlage "Analyse mit Codekorrektur" aus. Geben Sie einen Namen ein, und bestätigen Sie das Dialogfeld.

Die Vorlage öffnet eine DiagnosticAnalyzer.cs-Datei . Wählen Sie diese Editorpufferregisterkarte aus. Diese Datei verfügt über eine Analyseklasse (gebildet aus dem Namen, den Sie dem Projekt gegeben haben), die von DiagnosticAnalyzer (einem Roslyn-API-Typ) abgeleitet ist. Ihre neue Klasse verfügt über eine DiagnosticAnalyzerAttribute Deklarierung Der Analyse ist für die C#-Sprache relevant, sodass der Compiler Ihre Analyse erkennt und lädt.

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

Sie können eine Analyse mithilfe von Visual Basic implementieren, die auf C#-Code ausgerichtet ist und umgekehrt. Im DiagnosticAnalyzerAttribute ist es wichtiger, auszuwählen, ob die Analyse auf eine Sprache oder beides ausgerichtet ist. Komplexere Analyzer, die eine detaillierte Modellierung der Sprache erfordern, können nur auf eine einzelne Sprache abzielen. Wenn Ihre Analyse beispielsweise nur Typnamen oder öffentliche Membernamen überprüft, kann es möglich sein, das allgemeine Sprachmodell Roslyn in Visual Basic und C# zu verwenden. FxCop warnt beispielsweise, dass eine Klasse implementiert wird ISerializable, aber die Klasse hat das SerializableAttribute Attribut nicht sprachunabhängig und funktioniert sowohl für Visual Basic- als auch für C#-Code.

Initialisieren des Analyzers

Scrollen Sie ein wenig in der DiagnosticAnalyzer Klasse nach unten, um die Initialize Methode anzuzeigen. Der Compiler ruft diese Methode beim Aktivieren eines Analyzers auf. Die Methode verwendet ein AnalysisContext Objekt, mit dem Ihre Analyse kontextbezogene Informationen abrufen und Rückrufe für Ereignisse für die Arten von Code registrieren kann, die Sie analysieren möchten.

public override void Initialize(AnalysisContext context) {}

Öffnen Sie eine neue Zeile in dieser Methode, und geben Sie "Kontext" ein, um eine IntelliSense-Abschlussliste anzuzeigen. In der Vervollständigungsliste finden Sie viele Register... Methoden zum Behandeln verschiedener Arten von Ereignissen. Beispielsweise ruft der erste Code für einen Block zurück, RegisterCodeBlockActionder in der Regel zwischen geschweiften geschweiften Klammern codeiert. Bei der Registrierung für einen Block wird auch der Code für den Initialisierer eines Felds, den wert, der einem Attribut zugewiesen wird, oder der Wert eines optionalen Parameters zurückgegeben.

Als weiteres Beispiel RegisterCompilationStartActionrufen Sie den Code am Anfang einer Kompilierung zurück, was nützlich ist, wenn Sie den Zustand über viele Speicherorte sammeln müssen. Sie können eine Datenstruktur erstellen, z. B. um alle verwendeten Symbole zu sammeln, und jedes Mal, wenn die Analyse für eine syntax oder ein Symbol wieder aufgerufen wird, können Sie Informationen zu den einzelnen Speicherorten in Ihrer Datenstruktur speichern. Wenn Sie aufgrund des Kompilierungsendes wieder aufgerufen werden, können Sie alle speicherorte analysieren, die Sie gespeichert haben, um beispielsweise zu melden, welche Symbole der Code aus jeder using Anweisung verwendet.

Mithilfe der Syntaxschnellansicht haben Sie gelernt, dass Sie aufgerufen werden möchten, wenn der Compiler ein ObjectCreationExpression verarbeitet. Sie verwenden diesen Code zum Einrichten des Rückrufs:

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

Sie registrieren sich für einen Syntaxknoten und filtern nur nach Objekterstellungssyntaxknoten. In der Konvention verwenden Analyseautoren beim Registrieren von Aktionen eine Lambda-Funktion, die dazu beiträgt, Die Analysegeräte zustandslos zu halten. Sie können das Visual Studio-Feature "From Usage generieren" verwenden, um die AnalyzeObjectCreation Methode zu erstellen. Dadurch wird auch der richtige Kontextparametertyp generiert.

Festlegen von Eigenschaften für Benutzer Ihrer Analyse

Damit die Analyse in der Visual Studio-Benutzeroberfläche ordnungsgemäß angezeigt wird, suchen und ändern Sie die folgende Codezeile, um die Analyse zu identifizieren:

internal const string Category = "Naming";

Wechseln von "Naming" zu "API Guidance".

Suchen Und öffnen Sie die Datei "Resources.resx" in Ihrem Projekt mithilfe der Projektmappen-Explorer. Sie können eine Beschreibung für Ihren Analysator, Ihren Titel usw. einfügen. Sie können den Wert für all diese "Don't use ImmutableArray<T> constructor" Werte vorerst ändern. Sie können Zeichenfolgenformatierungsargumente in Ihre Zeichenfolge ({0}, {1}usw.) einfügen und später beim Aufrufen Diagnostic.Create()ein params Array von Argumenten angeben, die übergeben werden sollen.

Analysieren eines Objekterstellungsausdrucks

Die AnalyzeObjectCreation Methode verwendet einen anderen Kontexttyp, der vom Codeanalyseframework bereitgestellt wird. Mit der Initialize Methode AnalysisContext können Sie Aktionsrückrufe registrieren, um Die Analyse einzurichten. Das SyntaxNodeAnalysisContextBeispiel hat eine CancellationToken , die Sie passieren können. Wenn ein Benutzer mit der Eingabe im Editor beginnt, bricht Roslyn die Ausführung von Analyzern ab, um Arbeit zu sparen und die Leistung zu verbessern. Als weiteres Beispiel weist dieser Kontext eine Node-Eigenschaft auf, die den Syntaxknoten für die Objekterstellung zurückgibt.

Rufen Sie den Knoten ab, von dem Sie annehmen können, dass es sich um den Typ handelt, nach dem Sie die Syntaxknotenaktion gefiltert haben:

var objectCreation = (ObjectCreationExpressionSyntax)context.Node;

Starten von Visual Studio mit Der Analyse beim ersten Mal

Starten Sie Visual Studio, indem Sie Den Analyzer erstellen und ausführen (drücken Sie F5). Da das Startprojekt im Projektmappen-Explorer das VSIX-Projekt ist, erstellt Ihr Code Ihren Code und ein VSIX und startet dann Visual Studio mit diesem INSTALLIERTen VSIX. Wenn Sie Visual Studio auf diese Weise starten, wird sie mit einer eindeutigen Registrierungsstruktur gestartet, sodass Ihre Standard Verwendung von Visual Studio nicht von Ihren Testinstanzen beim Erstellen von Analysen betroffen ist. Wenn Sie visual Studio zum ersten Mal auf diese Weise starten, führt Visual Studio mehrere Initialisierungen wie beim ersten Starten von Visual Studio nach der Installation durch.

Erstellen Sie ein Konsolenprojekt, und geben Sie dann den Arraycode in die Hauptmethode der Konsolenanwendungen ein:

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

Die Codezeilen mit ImmutableArray Wellenstrichen, da Sie das unveränderliche NuGet-Paket abrufen und Ihrem Code eine using Anweisung hinzufügen müssen. Drücken Sie im Projektmappen-Explorer die rechte Zeigerschaltfläche auf dem Projektknoten, und wählen Sie "NuGet-Pakete verwalten" aus. Geben Sie im NuGet-Manager "Unveränderlich" in das Suchfeld ein, und wählen Sie im linken Bereich das Element "System.Collections.Immutable " (nicht " Microsoft.Bcl.Immutable") aus, und drücken Sie im rechten Bereich die Schaltfläche "Installieren ". Durch die Installation des Pakets wird ein Verweis auf Ihre Projektverweise hinzugefügt.

Sie sehen weiterhin rote Wellen unter ImmutableArray. Platzieren Sie also das Caret in diesem Bezeichner, und drücken Sie STRG+. (Punkt), um das vorgeschlagene Fixmenü anzuzeigen, und wählen Sie aus, um die entsprechende using Anweisung hinzuzufügen.

Speichern Sie "Alle", und schließen Sie die zweite Instanz von Visual Studio, um sie jetzt in einen sauber Zustand zu versetzen, um den Vorgang fortzusetzen.

Beenden der Analyse mithilfe von Bearbeitung und Fortfahren

Legen Sie in der ersten Instanz von Visual Studio einen Haltepunkt am Anfang der AnalyzeObjectCreation Methode fest, indem Sie F9 mit dem Caret in der ersten Zeile drücken.

Starten Sie die Analyse erneut mit F5, und öffnen Sie in der zweiten Instanz von Visual Studio Die Konsolenanwendung, die Sie zuletzt erstellt haben, erneut.

Sie kehren zur ersten Instanz von Visual Studio am Haltepunkt zurück, da der Roslyn-Compiler einen Objekterstellungsausdruck gesehen und in die Analyse aufgerufen hat.

Rufen Sie den Objekterstellungsknoten ab. Durchlaufen Sie die Zeile, die die objectCreation Variable durch Drücken von F10 festlegt, und im Direktfenster wird der Ausdruck "objectCreation.ToString()"ausgewertet. Sie sehen, dass der Syntaxknoten, auf den die Variable verweist, der Code "new ImmutableArray<int>()"ist, genau das, wonach Sie suchen.

Abrufen des ImmutableArray<T> Type-Objekts. Sie müssen überprüfen, ob der typ, der erstellt wird, immutableArray ist. Zuerst erhalten Sie das Objekt, das diesen Typ darstellt. Sie überprüfen Typen mithilfe des Semantikmodells, um sicherzustellen, dass Sie genau den richtigen Typ haben und die Zeichenfolge nicht vergleichen.ToString() Geben Sie die folgende Codezeile am Ende der Funktion ein:

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

Sie legen generische Typen in Metadaten mit Backticks (') und die Anzahl der generischen Parameter fest. Deshalb wird "... ImmutableArray<T>" im Metadatennamen.

Das semantische Modell enthält viele nützliche Dinge, mit denen Sie Fragen zu Symbolen, Datenfluss, variabler Lebensdauer usw. stellen können. Roslyn trennt Syntaxknoten aus verschiedenen technischen Gründen vom semantischen Modell (Leistung, Modellierung fehlerhafter Code usw.). Sie möchten, dass das Kompilierungsmodell Informationen in Verweisen nachschlagen soll, um einen genauen Vergleich zu erhalten.

Sie können den gelben Ausführungspunkt auf der linken Seite des Editorfensters ziehen. Ziehen Sie ihn bis zur Zeile, die die objectCreation Variable festlegt, und führen Sie mithilfe von F10 die neue Codezeile durch. Wenn Sie mit dem Mauszeiger auf die Variable immutableArrayOfTypezeigen, sehen Sie, dass der genaue Typ im Semantikmodell gefunden wurde.

Rufen Sie den Typ des Objekterstellungsausdrucks ab. "Typ" wird in diesem Artikel auf einige Arten verwendet, aber dies bedeutet, dass Sie ein Modell von Foo erhalten müssen, wenn Sie über einen "neuen Foo"-Ausdruck verfügen. Sie müssen den Typ des Objekterstellungsausdrucks abrufen, um festzustellen, ob es sich um den Typ ImmutableArray<T> handelt. Verwenden Sie das semantische Modell erneut, um Symbolinformationen für das Typsymbol (ImmutableArray) im Objekterstellungsausdruck abzurufen. Geben Sie die folgende Codezeile am Ende der Funktion ein:

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

Da die Analyse unvollständigen oder falschen Code in Editorpuffern verarbeiten muss (z. B. fehlt eine using Anweisung), sollten Sie prüfen, ob symbolInfo sie vorhanden ist null. Sie müssen einen benannten Typ (INamedTypeSymbol) aus dem Symbolinformationsobjekt abrufen, um die Analyse abzuschließen.

Vergleichen Sie die Typen. Da es einen offenen generischen T-Typ gibt, nach dem gesucht wird, und der Typ im Code ein konkreter generischer Typ ist, fragen Sie die Symbolinformationen ab, aus denen der Typ erstellt wird (ein offener generischer Typ), und vergleichen Sie dieses Ergebnis mit immutableArrayOfTType. Geben Sie am Ende der Methode Folgendes ein:

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

Melden Sie die Diagnose. Das Melden der Diagnose ist ziemlich einfach. Sie verwenden die regel, die für Sie in der Projektvorlage erstellt wurde, die vor der Initialize-Methode definiert ist. Da dies im Code ein Fehler ist, können Sie die Zeile ändern, die die initialisierte Regel ersetzt DiagnosticSeverity.Warning (grüne Wellenlinie) durch DiagnosticSeverity.Error (rote Wellenlinie). Der Rest der Regel initialisiert aus den Ressourcen, die Sie am Anfang der exemplarischen Vorgehensweise bearbeitet haben. Sie müssen auch die Position für die Wellenlinie melden, bei der es sich um die Position der Typspezifikation des Objekterstellungsausdrucks handelt. Geben Sie diesen Code in den Block ein if :

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

Ihre Funktion sollte wie folgt aussehen (möglicherweise anders formatiert):

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

Entfernen Sie den Haltepunkt, damit Ihre Analyse funktioniert (und beenden Sie die Rückkehr zur ersten Instanz von Visual Studio). Ziehen Sie den Ausführungspunkt an den Anfang der Methode, und drücken Sie F5 , um die Ausführung fortzusetzen. Wenn Sie zurück zur zweiten Instanz von Visual Studio wechseln, beginnt der Compiler, den Code erneut zu untersuchen, und er ruft ihren Analyzer auf. Sie können einen Wellenschalter unter ImmutableType<int>sehen.

Hinzufügen einer "Codekorrektur" für das Codeproblem

Schließen Sie zunächst die zweite Instanz von Visual Studio, und beenden Sie das Debuggen in der ersten Instanz von Visual Studio (wo Sie die Analyse entwickeln).

Fügen Sie eine neue Klasse hinzu. Verwenden Sie im Projektmappen-Explorer das Kontextmenü (schaltfläche für den rechten Zeiger) auf dem Projektknoten, und wählen Sie ein neues Element aus. Fügen Sie eine Klasse mit dem Namen BuildCodeFixProviderhinzu. Diese Klasse muss von CodeFixProvider, und Sie müssen STRG+ verwenden. (Punkt) zum Aufrufen der Codekorrektur, die die richtige using Anweisung hinzufügt. Diese Klasse muss auch mit ExportCodeFixProvider Einem Attribut versehen werden, und Sie müssen eine using Anweisung hinzufügen, um die LanguageNames Enumeration aufzulösen. Sie sollten über eine Klassendatei mit dem folgenden Code verfügen:

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

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

Abgeleitete Elemente stub out. Platzieren Sie nun das Caret des Editors in den BezeichnerCodeFixProvider, und drücken Sie STRG+. (Punkt), um die Implementierung für diese abstrakte Basisklasse zu entfernen. Dadurch wird eine Eigenschaft und eine Methode für Sie generiert.

Implementiert die -Eigenschaft. Füllen Sie den Text der FixableDiagnosticIds Eigenschaft get mit dem folgenden Code aus:

return ImmutableArray.Create(ImmutableArrayAnalyzerAnalyzer.DiagnosticId);

Roslyn vereint Diagnose und Fixes, indem diese Bezeichner übereinstimmen, die nur Zeichenfolgen sind. Die Projektvorlage hat eine Diagnose-ID für Sie generiert, und Sie können sie ändern. Der Code in der Eigenschaft gibt einfach die ID aus der Analyseklasse zurück.

Die RegisterCodeFixAsync-Methode verwendet einen Kontext. Der Kontext ist wichtig, da eine Codekorrektur auf mehrere Diagnose angewendet werden kann, oder es kann mehrere Probleme in einer Codezeile geben. Wenn Sie "context" im Textkörper der Methode eingeben, werden In der IntelliSense-Abschlussliste einige nützliche Member angezeigt. Es gibt ein CancellationToken-Mitglied, das Sie überprüfen können, um zu überprüfen, ob etwas den Fix abbrechen möchte. Es gibt ein Dokumentmember mit vielen nützlichen Membern und ermöglicht Es Ihnen, zu den Projekt- und Lösungsmodellobjekten zu gelangen. Es gibt ein Span-Element, das das Start- und Ende des Codespeicherorts darstellt, der angegeben wird, wenn Sie die Diagnose gemeldet haben.

Stellen Sie die Methode als asynchron fest. Als Erstes müssen Sie die generierte Methodendeklaration async als Methode beheben. Die Codekorrektur für das Stubbing der Implementierung einer abstrakten Klasse enthält nicht die async Schlüsselwort (keyword), obwohl die Methode eine Task.

Rufen Sie den Stamm der Syntaxstruktur ab. Um Code zu ändern, müssen Sie eine neue Syntaxstruktur mit den Änderungen erstellen, die der Codekorrektur vorgenommen hat. Sie benötigen den Document Kontext zum Aufrufen GetSyntaxRootAsync. Dies ist eine asynchrone Methode, da unbekannt ist, um die Syntaxstruktur abzurufen, z. B. das Abrufen der Datei vom Datenträger, analysieren und das Roslyn-Codemodell dafür erstellen. Die Visual Studio-Benutzeroberfläche sollte während dieser Zeit reaktionsfähig sein, wodurch die Verwendung async aktiviert wird. Ersetzen Sie die Codezeile in der Methode durch Folgendes:

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

Suchen Sie den Knoten mit dem Problem. Sie übergeben die Kontextspanne, aber der gefundene Knoten ist möglicherweise nicht der Code, den Sie ändern müssen. Die gemeldete Diagnose hat nur die Spanne für den Typbezeichner (wo die Wellenlinie gehört), aber Sie müssen den gesamten Objekterstellungsausdruck ersetzen, einschließlich der new Schlüsselwort (keyword) am Anfang und den Klammern am Ende. Fügen Sie der Methode den folgenden Code hinzu (und verwenden Sie STRG+, um eine using Anweisung für ):ObjectCreationExpressionSyntax

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

Registrieren Sie ihren Code fix für die Glühbirne-UI. Wenn Sie ihren Code fix registrieren, wird Roslyn automatisch in die Visual Studio-Glühbirne-Benutzeroberfläche eingebunden. Endbenutzer werden sehen, dass sie STRG+ verwenden können. (Punkt), wenn ihr Analysegerät einen ungültigen ImmutableArray<T> Konstruktor verwendet. Da ihr Codekorrekturanbieter nur ausgeführt wird, wenn ein Problem aufgetreten ist, können Sie davon ausgehen, dass Sie den gesuchten Objekterstellungsausdruck haben. Über den Kontextparameter können Sie die neue Codekorrektur registrieren, indem Sie den folgenden Code am Ende der RegisterCodeFixAsync Methode hinzufügen:

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

Sie müssen die Caret des Editors in den Bezeichner setzen und CodeActiondann STRG+ verwenden. (Punkt) fügen Sie die entsprechende using Anweisung für diesen Typ hinzu.

Platzieren Sie dann das Caret des Editors in der ChangeToImmutableArrayEmpty ID, und verwenden Sie strg. Verwenden Sie dann erneut STRG+, um diesen Methodenstub für Sie zu generieren.

Dieser letzte Codeausschnitt, den Sie hinzugefügt haben, registriert die Codekorrektur, indem eine CodeAction und die Diagnose-ID für die Art des gefundenen Problems übergeben wird. In diesem Beispiel gibt es nur eine Diagnose-ID, für die dieser Code Korrekturen bereitstellt, sodass Sie einfach das erste Element des Diagnose-IDs-Arrays übergeben können. Wenn Sie das CodeActionObjekt erstellen, übergeben Sie den Text, den die Glühbirne-UI als Beschreibung des Codekorrekturen verwenden soll. Sie übergeben auch eine Funktion, die ein CancellationToken akzeptiert und ein neues Dokument zurückgibt. Das neue Dokument verfügt über eine neue Syntaxstruktur, die den gepatchten Code enthält, der aufgerufen wird ImmutableArray.Empty. Dieser Codeausschnitt verwendet eine Lambda-Funktion, sodass sie den ObjectCreation-Knoten und das Dokument des Kontexts schließen kann.

Erstellen Sie die neue Syntaxstruktur. Geben Sie in der ChangeToImmutableArrayEmpty Methode, deren Stub Sie zuvor generiert haben, die Codezeile ein: ImmutableArray<int>.Empty;. Wenn Sie das Toolfenster "Syntaxschnellansicht " erneut anzeigen, sehen Sie, dass es sich bei dieser Syntax um einen SimpleMemberAccessExpression-Knoten handelt. Dies ist das, was diese Methode in einem neuen Dokument erstellen und zurückgeben muss.

Die erste Änderung besteht ChangeToImmutableArrayEmpty darin, vorher Task<Document> hinzuzufügenasync, da die Codegeneratoren nicht davon ausgehen können, dass die Methode asynchron sein soll.

Füllen Sie den Textkörper mit dem folgenden Code aus, damit die Methode wie folgt aussieht:

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

Sie müssen die Caret des Editors in den SyntaxGenerator Bezeichner setzen und STRG+ verwenden. (Punkt) fügen Sie die entsprechende using Anweisung für diesen Typ hinzu.

Dieser Code verwendet SyntaxGeneratoreinen nützlichen Typ zum Erstellen neuer Code. Nach dem Abrufen eines Generators für das Dokument, das das Codeproblem aufweist, ChangeToImmutableArrayEmpty wird der Typ aufgerufen MemberAccessExpression, auf den wir zugreifen möchten, und den Namen des Elements als Zeichenfolge übergeben.

Als Nächstes ruft die Methode den Stamm des Dokuments ab, und da dies im allgemeinen Fall beliebige Arbeit umfassen kann, wartet der Code auf diesen Aufruf und übergibt das Abbruchtoken. Roslyn-Codemodelle sind unveränderlich, z. B. das Arbeiten mit einer .NET-Zeichenfolge; Wenn Sie die Zeichenfolge aktualisieren, erhalten Sie ein neues Zeichenfolgenobjekt zurück. Wenn Sie aufrufen ReplaceNode, erhalten Sie einen neuen Stammknoten zurück. Der Großteil der Syntaxstruktur wird freigegeben (da sie unveränderlich ist), aber der objectCreation Knoten wird durch den memberAccess Knoten ersetzt, sowie alle übergeordneten Knoten bis zum Syntaxbaumstamm.

Probieren Sie ihren Code fix aus

Sie können nun F5 drücken, um die Analyse in einer zweiten Instanz von Visual Studio auszuführen. Öffnen Sie das konsolenprojekt, das Sie zuvor verwendet haben. Nun sollte die Glühbirne angezeigt werden, wo ihr neuer Objekterstellungsausdruck für ImmutableArray<int>sie ist. Wenn Sie STRG+ drücken. (Punkt), wird ihr Codekorrektur angezeigt, und in der Benutzeroberfläche der Glühbirne wird eine automatisch generierte Codedifferenzvorschau angezeigt. Roslyn schafft dies für Sie.

Pro-Tipp: Wenn Sie die zweite Instanz von Visual Studio starten und die Glühbirne mit Ihrem Codekorrektur nicht angezeigt wird, müssen Sie möglicherweise den Visual Studio-Komponentencache löschen. Durch das Löschen des Caches wird Visual Studio gezwungen, die Komponenten erneut zu untersuchen, sodass Visual Studio ihre neueste Komponente aufnehmen sollte. Fahren Sie zunächst die zweite Instanz von Visual Studio herunter. Navigieren Sie dann im Windows-Explorer zu %LOCALAPPDATA%\Microsoft\VisualStudio\16.0Roslyn\. (Die "16.0" wird von Version zu Version mit Visual Studio geändert.) Löschen Sie das Unterverzeichnis ComponentModelCache.

Video besprechen und Codeprojekt fertig stellen

Hier sehen Sie den gesamten fertigen Code. Die Unterordner DoNotUseImmutableArrayCollectionInitializer und DoNotUseImmutableArrayCtor verfügen jeweils über eine C#-Datei zum Suchen von Problemen und eine C#-Datei, die die Codefixes implementiert, die in der Visual Studio-Glühbirne-Benutzeroberfläche angezeigt werden. Beachten Sie, dass der fertige Code etwas mehr Abstraktion aufweist, um das Abrufen des ImmutableArray<T-Typobjekts> immer wieder zu vermeiden. Es verwendet geschachtelte registrierte Aktionen, um das Typobjekt in einem Kontext zu speichern, der verfügbar ist, wenn die Unteraktionen (Analysieren der Objekterstellung und Analyse von Sammlungsinitialisierungen) ausgeführt werden.