Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Die .NET Compiler Platform ("Roslyn") hilft Ihnen beim Erstellen codefähiger Bibliotheken. Eine codeabhängige Bibliothek bietet Funktionen, die Sie verwenden können, und Tools (Roslyn-Analysetools), die helfen, um die Bibliothek optimal zu nutzen und Fehler zu vermeiden. In diesem Thema erfahren Sie, wie Sie eine echte Roslyn-Analyse erstellen, um häufige Fehler beim Verwenden des System.Collections.Immutable NuGet-Pakets 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.
Loslegen
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 Visual Studio Extensibility Tools unter Common 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 Hauptmenü Datei>Neue>Projectwechseln, C#- im linken Navigationsbereich auswählen und dann Erweiterbarkeitauswählen. Wenn Sie die Projektvorlage "Installieren der Visual Studio-Erweiterbarkeitstools" Breadcrumb-Projektvorlage auswählen, werden Sie aufgefordert, das SDK herunterzuladen und zu installieren.
- .NET Compiler Platform SDK („Roslyn“). Sie können dieses SDK auch installieren, indem Sie zum Hauptmenü Datei>Neue>Projectwechseln, C#- im linken Navigationsbereich auswählen und dann Erweiterbarkeitauswählen. Wenn Sie die ".NET Compiler Platform SDK" Navigationspfad-Projektvorlage auswählen, werden Sie aufgefordert, das SDK herunterzuladen und zu installieren. Dieses SDK enthält den Roslyn Syntax-Visualizer. Dieses nützliche Tool hilft Ihnen, herauszufinden, welche Code-Modelltypen 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.
Was ist das Problem?
Stellen Sie sich vor, dass Sie eine Bibliothek mit Unterstützung für ImmutableArray (z. B. System.Collections.Immutable.ImmutableArray<T>) bereitstellen. 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 gewohnte Qualitätserfahrung ist, die sie in Visual Studio mit .NET kennen.
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 zur Laufzeit führt jedoch zu einem Absturz:
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. Strukturen müssen parameterlose Konstruktoren aufweisen, damit default(T)-Ausdrücke Strukturen mit allen Null- oder NULL-Elementen zurückgeben können. Wenn der Code auf b1.Lengthzugreift, 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, erhalten Sie beim Hinzufügen eines neuen Elements ein neues ImmutableArray-Objekt. Dieses kann aus Leistungsgründen Speicher mit einem bereits vorhandenen ImmutableArray gemeinsam nutzen. Da b2 auf das erste ImmutableArray verweist, bevor Add() fünfmal aufgerufen wird, ist b2 ein Standard-ImmutableArray. Das Aufrufen von „Länge“ führt ebenfalls zu einem Absturz mit einem NULL-Dereferenzierungsfehler. Die richtige Methode zum Initialisieren eines UnveränderlichenArrays ohne manuelles Aufrufen von "Add" besteht darin, ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5})zu verwenden.
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 Syntax Visualizer über das Menü Ansicht>Sonstige – Windows>Roslyn Syntax Visualizer.
Platzieren Sie den Cursor des Editors in der Zeile, die b1 deklariert. Syntax Visualizer zeigt an, dass Sie sich in einem LocalDeclarationStatement-Knoten der Syntaxstruktur befinden. Dieser Knoten verfügt über eine VariableDeclaration, die wiederum eine VariableDeclaratorhat, die wiederum eine EqualsValueClausehat, und schließlich eine ObjectCreationExpression. Wenn Sie im Baum der Knoten des Syntaxvisualisierers klicken, wird die Syntax im Editorfenster hervorgehoben, um den Code anzuzeigen, 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 Hauptmenü Datei>Neues>Projektaus. Klicken Sie im Dialogfeld Neues Projekt unter den C#-Projekten auf der linken Navigationsleiste auf Erweiterbarkeit, und wählen Sie im rechten Bereich die Projektvorlage Analysetool mit Codefix aus. Geben Sie einen Namen ein, und bestätigen Sie das Dialogfeld.
Die Vorlage öffnet eine DiagnosticAnalyzer.cs Datei. Wählen Sie den Tab für den Editorpuffer aus. Diese Datei enthält 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-Deklaration, die Ihren Analysator als relevant für die C#-Sprache kennzeichnet, sodass der Compiler Ihren Analysator erkennt und lädt.
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ImmutableArrayAnalyzer : 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 Ihr Analysetool beispielsweise nur Typnamen oder öffentliche Membernamen überprüft, könnte es möglich sein, das von Roslyn angebotene allgemeine Sprachmodell in Visual Basic und C# zu verwenden. FxCop warnt beispielsweise, dass eine Klasse ISerializableimplementiert, aber nicht das Attribut SerializableAttribute besitzt. Diese Warnung ist sprachunabhängig und funktioniert sowohl für Visual Basic- als auch für C#-Code.
Initialisieren des Analyzers
Scrollen Sie 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, das Ihrem Analysegerät ermöglicht, kontextbezogene Informationen abzurufen und Callbacks für Ereignisse in Bezug auf die Arten von Code zu registrieren, 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 gibt es viele Register...-Methoden für den Umgang mit verschiedenen Arten von Ereignissen. Die erste Methode (RegisterCodeBlockAction) führt beispielsweise einen Rückruf zu Ihrem Code für einen Block aus, wobei es sich in der Regel um Code in geschweiften Klammern handelt. Bei der Registrierung für einen Block erfolgt auch ein Rückruf zu Ihrem Code für den Initialisierer eines Feldes, den Wert, der einem Attribut zugeordnet wird, oder den Wert eines optionalen Parameters.
Ein weiteres Beispiel: RegisterCompilationStartActionruft Ihren Code zu Beginn einer Kompilierung auf, was nützlich ist, wenn Sie den Status an vielen Orten 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 gespeicherten Speicherorte analysieren, um beispielsweise zu melden, welche Symbole der Code aus jeder using-Anweisung verwendet.
Mithilfe des Syntax-Visualisierershaben Sie gelernt, dass Sie aufgerufen werden möchten, wenn der Compiler einen 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. Üblicherweise nutzen Personen, die Analysetools erstellen, beim Registrieren von Aktionen eine Lambdafunktion, die dazu beiträgt, dass die Analysetools statusfrei sind. Sie können das Visual Studio-Feature Generate From Usage verwenden, um die AnalyzeObjectCreation-Methode zu erstellen. Dadurch wird auch der richtige Kontextparametertyp für Sie 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";
Ändern Sie "Naming" in "API Guidance".
Suchen und öffnen Sie als Nächstes die Datei Resources.resx in Ihrem Projekt unter Verwendung des Projektmappen-Explorers. Sie können beispielsweise eine Beschreibung für Ihr Analysetool und Ihren Titel einfügen. Zudem haben Sie die Möglichkeit, den Wert für all diese Elemente vorläufig in "Don't use ImmutableArray<T> constructor" zu ändern. Sie können Zeichenfolgenformatierungsargumente in Ihre Zeichenfolge einfügen ({0}, {1}usw.), und später, wenn Sie Diagnostic.Create()aufrufen, können Sie ein params Array von Argumenten angeben, das übergeben werden soll.
Analysieren eines Objekterstellungsausdrucks
Die AnalyzeObjectCreation-Methode verwendet einen anderen Kontexttyp, der vom Codeanalyseframework bereitgestellt wird. Mit dem Initialize der AnalysisContext-Methode können Sie Aktionsrückrufe zum Einrichten Ihres Analysetools registrieren. Der SyntaxNodeAnalysisContext verfügt beispielsweise über ein CancellationToken, das Sie umgehen 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 Sie Visual Studio das erste Mal mit Ihrem Analyzer.
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, wird beim Ausführen Ihres Codes Ihr Code sowie VSIX-Code kompiliert, und dann wird Visual Studio mit der installierten VSIX-Komponente gestartet. Wenn Sie Visual Studio auf diese Weise starten, wird die Komponente mit einer eindeutigen Registrierungsstruktur gestartet, sodass Ihre Hauptverwendung von Visual Studio während des Erstellens von Analysetools nicht durch Ihre Testinstanzen beeinträchtigt wird. 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 Main-Methode der Konsolenanwendung 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 weisen Wellenlinien auf, da Sie das unveränderliche NuGet-Paket abrufen und ihrem Code eine using-Anweisung hinzufügen müssen. Klicken Sie mit der rechten Maustaste auf den Projektknoten im Projektmappen-Explorer, und wählen Sie NuGet-Pakete verwalten aus. Geben Sie im NuGet-Manager "Unveränderlich" in das Suchfeld ein, und wählen Sie das Element System.Collections.Immutable (wählen Sie nicht Microsoft.Bcl.Immutable) im linken Bereich 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.
Unter ImmutableArray werden weiterhin rote Wellenlinien angezeigt. Platzieren Sie also den Cursor in diesem Bezeichner, und drücken Sie STRG+. (Punkt), um das vorgeschlagene Menü für Fixes anzuzeigen. Fügen Sie anschließend die entsprechende using-Anweisung hinzu.
Speichern Sie alle Elemente, und schließen Sie die zweite Instanz von Visual Studio vorerst, um einen fehlerfreien Zustand zu erreichen.
Beenden des Analysetools mithilfe von „Bearbeiten und fortfahren“
Legen Sie in der ersten Instanz von Visual Studio am Anfang der AnalyzeObjectCreation-Methode einen Haltepunkt 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, weil der Roslyn-Compiler eine Objekterstellung erkannt und Ihren Analyzer aufgerufen hat.
Rufen Sie den Objekterstellungsknoten ab. Überspringen Sie die Zeile, die die objectCreation-Variable festlegt, indem Sie F10 drücken, und werten Sie im Direktfenster den Ausdruck "objectCreation.ToString()" aus. Sie sehen, dass der Syntaxknoten, auf den die Variable verweist, der Code "new ImmutableArray<int>()"ist, genau das, wonach Sie suchen.
Rufen Sie das Objekt vom Typ „ImmutableArray<T>“ ab. 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 aus ToString()vergleichen. 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 sehen Sie "...ImmutableArray<T>" nicht 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ührungszeiger auf der linken Seite des Editor-Fensters ziehen. Ziehen Sie es bis zur Zeile, die die objectCreation Variable festlegt, und überspringen Sie Ihre neue Codezeile mit F10. 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 verschiedene Weisen verwendet. Dies bedeutet, dass Sie bei einem Ausdruck wie „new Foo“ ein Modell von Foo benötigen. Sie müssen den Typ des Objekterstellungsausdrucks abrufen, um festzustellen, ob es sich um den ImmutableArray-<T->-Typ 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 Ihr Analysierer unvollständigen oder fehlerhaften Code in Editorpuffern behandeln muss (z. B. fehlt eine using-Anweisung), sollten Sie überprüfen, ob symbolInfonullist. 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 wir suchen, 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 und vor der Initialize-Methode definiert ist. Da dies im Code ein Fehler ist, können Sie die Zeile ändern, die „Rule“ initialisiert hat, um DiagnosticSeverity.Warning (grüne Wellenlinie) durch DiagnosticSeverity.Error (rote Wellenlinie) zu ersetzen. Der restliche Teil von „Rule“ wird anhand der Ressourcen initialisiert, die Sie am Anfang der exemplarischen Vorgehensweise bearbeitet haben. Sie müssen auch die Position der Wellenlinie melden, die der Position der Typspezifikation des Objekterstellungsausdrucks entspricht. Geben Sie diesen Code in den if-Block ein:
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 Sie sehen können, wie Ihr Analysetool arbeitet (und nicht zur ersten Instanz von Visual Studio zurückkehren). Ziehen Sie den Ausführungszeiger an den Anfang Ihrer 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. Unter ImmutableType<int> wird eine Wellenlinie angezeigt.
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ü (rechte Maustaste) für Ihr Projekt, und wählen Sie die Option zum Hinzufügen eines neuen Elements aus. Fügen Sie eine Klasse namens BuildCodeFixProviderhinzu. Diese Klasse muss von CodeFixProvider abgeleitet werden, und Sie müssen STRG+. (Punkt) verwenden, um den Codefix aufzurufen, der die richtige using-Anweisung hinzufügt. Diese Klasse muss auch mit dem ExportCodeFixProvider-Attribut annotiert 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
{}
Verkürzen Sie abgeleitete Member. Platzieren Sie nun den Cursor des Editors im Bezeichner CodeFixProvider, und drücken Sie STRG+. (Punkt), um die Implementierung für diese abstrakte Basisklasse zu verkürzen. Dadurch wird eine Eigenschaft und eine Methode für Sie generiert.
Implementieren Sie die Eigenschaft. Füllen Sie den FixableDiagnosticIds-Textkörper der get-Eigenschaft mit dem folgenden Code aus:
return ImmutableArray.Create(ImmutableArrayAnalyzer.DiagnosticId);
Roslyn vereint Diagnosen und Korrekturen, indem es diese Bezeichner, die einfach Zeichenfolgen sind, abgleicht. 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 Diagnosen 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-Vervollständigungsliste einige nützliche Member angezeigt. Es gibt einen CancellationToken-Member, den Sie überprüfen können, um zu ermitteln, ob ein Element den Fix abbrechen möchte. Es gibt ein Dokument-Element mit vielen nützlichen Elementen, und es ermöglicht Ihnen, auf die Projekt- und Lösungsmodellobjekte zuzugreifen. Es gibt einen Span-Member, der den Start und das Ende der Codeposition darstellt und angegeben wird, wenn Sie die Diagnose gemeldet haben.
Stellen Sie die Methode als asynchron fest. Als Erstes müssen Sie die generierte Methodendeklaration in eine async-Methode ändern. Der Codefix für das Verkürzen der Implementierung einer abstrakten Klasse enthält nicht das Schlüsselwort async, auch wenn die Methode ein Task-Element zurückgibt.
Rufen Sie den Stamm der Syntaxstruktur ab. Um Code zu ändern, müssen Sie einen neuen Syntaxbaum mit den Änderungen erstellen, die durch Ihre Codekorrektur vorgenommen werden. Sie benötigen das Document aus dem Kontext, um GetSyntaxRootAsync aufzurufen. Dies ist eine asynchrone Methode, weil unklar ist, welche Arbeit erforderlich ist, um die Syntaxstruktur abzurufen (unter Umständen einschließlich des Abrufens der Datei vom Datenträger, der Analyse und der Erstellung des Roslyn-Codemodells). Die Visual Studio-Benutzeroberfläche sollte während dieser Zeit reaktionsfähig sein, was durch die Nutzung von async ermöglicht 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 den Bereich des Typbezeichners bereitgestellt (zu dem die Wellenlinie gehört), aber Sie müssen den gesamten Objekterstellungsausdruck ersetzen (einschließlich des Schlüsselworts new am Anfang und der Klammern am Ende). Fügen Sie der Methode den folgenden Code hinzu (und verwenden Sie Ctrl+., um eine using-Anweisung für ObjectCreationExpressionSyntaxhinzuzufügen):
var objectCreation = root.FindNode(context.Span)
.FirstAncestorOrSelf<ObjectCreationExpressionSyntax>();
Registrieren Sie Ihren Codefix für die Glühbirnen-Benutzeroberfläche. Wenn Sie Ihren Codefix registrieren, wird Roslyn automatisch in die Fehlerbehebungs-Benutzeroberfläche in Visual Studio eingebunden. Endbenutzende erkennen, dass sie STRG+. (Punkt) verwenden können, wenn Ihr Analysetool mithilfe von Wellenlinien auf eine fehlerhafte Verwendung des ImmutableArray<T>-Konstruktors hinweist. Da Ihr Codefixanbieter nur ausgeführt wird, wenn ein Problem vorliegt, können Sie davon ausgehen, dass Sie über den gewünschten Objekterstellungsausdruck verfügen. Im 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 den Cursor des Editors im CodeAction-Bezeichner platzieren und anschließend mithilfe von STRG+. (Punkt) die entsprechende using-Anweisung für diesen Typ hinzufügen.
Platzieren Sie dann den Cursor des Editors im ChangeToImmutableArrayEmpty-Bezeichner, und verwenden Sie 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 werden. 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 CodeActionerstellen, übergeben Sie den Text, den die Glühbirne-UI als Beschreibung der Codekorrektur verwenden soll. Sie übergeben auch eine Funktion, die ein CancellationToken-Element verwendet und ein neues Document-Element zurückgibt. Das neue Dokument hat einen neuen Syntaxbaum, der Ihren gepatchten Code enthält, der ImmutableArray.Emptyaufruft. Dieser Codeschnipsel verwendet eine Lambdafunktion, sodass das Schließen über den objectCreation-Knoten und das Document-Element des Kontexts möglich ist.
Erstellen Sie die neue Syntaxstruktur. Geben Sie in der ChangeToImmutableArrayEmpty-Methode, deren Stub Sie zuvor generiert haben, die folgende Codezeile ein: ImmutableArray<int>.Empty;. Wenn Sie das Toolfenster Syntax-Visualizer erneut anzeigen, können Sie sehen, dass diese Syntax ein SimpleMemberAccessExpression-Knoten ist. Dies ist das, was diese Methode in einem neuen Dokument erstellen und zurückgeben muss.
Die erste Änderung an ChangeToImmutableArrayEmpty besteht darin, async vor Task<Document> hinzuzufügen, 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 den Cursor des Editors im SyntaxGenerator-Bezeichner platzieren und mithilfe von STRG+. (Punkt) die entsprechende using-Anweisung für diesen Typ hinzufügen.
Dieser Code verwendet SyntaxGenerator, bei dem es sich um einen nützlichen Typ zum Erstellen von neuem Code handelt. Nachdem ein Generator für das Dokument abgerufen wurde, das ein Codeproblem aufweist, wird ChangeToImmutableArrayEmpty von MemberAccessExpression aufgerufen. Auf diese Weise wird der Typ, der über den Member verfügt, auf den Sie zugreifen möchten, und der Name des Members 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 ReplaceNodeaufrufen, 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 sowie alle übergeordneten Knoten bis zum Syntaxstrukturstamm ersetzt.
Testen von Codefixes
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 an dem Ort erscheinen, wo sich Ihr neuer Objekterstellungsausdruck für ImmutableArray<int>befindet. Wenn Sie STRG+. (Punkt) drücken, wird der Codefix angezeigt. Zudem wird auf der Fehlerbehebungs-Benutzeroberfläche eine Differenzvorschau für einen automatisch generierten Code angezeigt. Roslyn schafft dies für Sie.
Praxistipp: Wenn Sie die zweite Instanz von Visual Studio starten und die Glühbirne mit Ihrer Codekorrektur nicht angezeigt wird, müssen Sie möglicherweise den Cache der Visual Studio-Komponenten löschen. Das Löschen des Caches zwingt Visual Studio dazu, die Komponenten erneut zu prüfen, sodass Visual Studio die neueste Komponente verwenden sollte. Schließen Sie zunächst die zweite Instanz von Visual Studio. Navigieren Sie dann in 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
Sie können den gesamten fertigen Code hiersehen. Die Unterordner DoNotUseImmutableArrayCollectionInitializer und DoNotUseImmutableArrayCtor verfügen jeweils über eine C#-Datei zum Suchen von Problemen und eine C#-Datei, die die Codebehebungen implementiert, die in der Visual Studio-Glühbirnen-Benutzeroberfläche angezeigt werden. Beachten Sie, dass der fertige Code etwas mehr Abstraktion hat, 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.