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.
In diesem Lernprogramm wird davon ausgegangen, dass Sie mit der Syntax-API vertraut sind. Der Artikel Einführung in die Syntaxanalyse bietet eine ausreichende Einführung.
In diesem Lernprogramm erkunden Sie die Symbol- und Bindungs-APIs. Diese APIs stellen Informationen zur semantischen Bedeutung eines Programms bereit. Sie ermöglichen es Ihnen, Fragen zu den Typen zu stellen und zu beantworten, die durch ein beliebiges Symbol in Ihrem Programm dargestellt werden.
Sie müssen das .NET Compiler Platform SDK installieren:
Installationsanweisungen – Visual Studio Installer
Es gibt zwei verschiedene Möglichkeiten, das .NET Compiler Platform SDK im Visual Studio Installer zu finden:
Installieren mit dem Visual Studio Installer – Arbeitslastenansicht
Das .NET Compiler Platform SDK wird nicht automatisch als Teil der Visual Studio-Erweiterungsentwicklungslast ausgewählt. Sie müssen sie als optionale Komponente auswählen.
- Ausführen des Visual Studio-Installationsprogramms
- Wählen Sie "Ändern" aus.
- Überprüfen Sie die Arbeitsauslastung der Visual Studio-Erweiterungsentwicklung .
- Öffnen Sie den Knoten Visual Studio-Extensionentwicklung in der Zusammenfassungsstruktur.
- Stellen Sie sicher, dass das Kontrollkästchen für .NET Compiler Platform SDK aktiviert ist.
- Wählen Sie Ändern aus.
Optional möchten Sie auch, dass der DGML-Editor Diagramme in der Visualisierung anzeigt:
- Öffnen Sie den Knoten „Einzelne Komponenten“ im Zusammenfassungsbaum.
- Aktivieren Sie das Kontrollkästchen für den DGML-Editor.
Installieren mit dem Visual Studio Installer – Registerkarte "Einzelne Komponenten"
- Ausführen des Visual Studio-Installationsprogramms
- Wählen Sie "Ändern" aus.
- Registerkarte "Einzelne Komponenten " auswählen
- Aktivieren Sie das Kontrollkästchen für .NET Compiler Platform SDK. Sie finden sie oben im Abschnitt "Compiler", "Buildtools" und "Laufzeiten" .
- Wählen Sie Ändern aus.
Optional möchten Sie auch, dass der DGML-Editor Diagramme in der Visualisierung anzeigt:
- Aktivieren Sie das Kontrollkästchen für den DGML-Editor. Sie finden sie im Abschnitt "Codetools ".
Grundlegendes zu Kompilierungen und Symbolen
Wenn Sie mit dem .NET Compiler SDK arbeiten, sind Sie mit den Unterschieden zwischen Syntax-API und der Semantik-API vertraut. Mit der Syntax-API können Sie die Struktur eines Programms betrachten. Häufig möchten Sie jedoch umfangreichere Informationen über die Semantik oder Bedeutung eines Programms. Während eine lose Codedatei oder ein Codeausschnitt von Visual Basic oder C#-Code syntaktisch isoliert analysiert werden kann, ist es nicht sinnvoll, Fragen wie "was ist der Typ dieser Variablen" in einem Vakuum zu stellen. Die Bedeutung eines Typnamens kann von Assemblyverweise, Namespaceimporten oder anderen Codedateien abhängig sein. Diese Fragen werden mithilfe der Semantik-API beantwortet, insbesondere der Microsoft.CodeAnalysis.Compilation Klasse.
Eine Instanz von Compilation ist analog zu einem einzelnen Projekt, wie vom Compiler gesehen und stellt alles dar, was zum Kompilieren eines Visual Basic- oder C#-Programms erforderlich ist. Die Kompilierung enthält den Satz von Quelldateien, die kompiliert werden sollen, Assemblyverweise und Compileroptionen. Sie können über die Bedeutung des Codes mit allen anderen Informationen in diesem Kontext nachdenken. Mithilfe von CompilationSymbolen können Sie Entitäten wie Typen, Namespaces, Member und Variablen finden, auf die Namen und andere Ausdrücke verweisen. Der Vorgang zum Zuordnen von Namen und Ausdrücken zu Symbolen wird als Bindung bezeichnet.
Wie Microsoft.CodeAnalysis.SyntaxTree, Compilation ist eine abstrakte Klasse mit sprachspezifischen Ableitungen. Beim Erstellen einer Instanz der Kompilierung müssen Sie eine Factorymethode für die Microsoft.CodeAnalysis.CSharp.CSharpCompilation (oder Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation) Klasse aufrufen.
Abfragen von Symbolen
In diesem Lernprogramm sehen Sie sich das Programm "Hello World" erneut an. Dieses Mal fragen Sie die Symbole im Programm ab, um zu verstehen, welche Typen diese Symbole darstellen. Sie fragen nach den Typen in einem Namespace ab, und erfahren Sie, welche Methoden für einen Typ verfügbar sind.
Sie können den fertigen Code für dieses Beispiel in unserem GitHub-Repository sehen.
Hinweis
Die Syntaxstrukturtypen verwenden die Vererbung, um die verschiedenen Syntaxelemente zu beschreiben, die an verschiedenen Stellen im Programm gültig sind. Die Verwendung dieser APIs bedeutet häufig, dass Eigenschaften oder Sammlungsmitglieder in bestimmte abgeleitete Typen umgewandelt werden. In den folgenden Beispielen sind die Zuordnung und die Umwandlungen separate Anweisungen, wobei explizit eingegebene Variablen verwendet werden. Sie können den Code lesen, um die Rückgabetypen der API und den Laufzeittyp der zurückgegebenen Objekte anzuzeigen. In der Praxis ist es häufiger, implizit typierte Variablen zu verwenden und auf API-Namen zu basieren, um den Typ der zu untersuchenden Objekte zu beschreiben.
Erstellen Sie ein neues C# Projekt für ein eigenständiges Code-Analysetool.
- Wählen Sie in Visual Studio "Datei>Neu>Projekt" aus, um das Dialogfeld "Neues Projekt" anzuzeigen.
- Wählen Sie unter Visual C#>ErweiterbarkeitStand-Alone Codeanalysetool aus.
- Benennen Sie Ihr Projekt "SemanticQuickStart", und klicken Sie auf "OK".
Sie werden das zuvor gezeigte grundlegende "Hello World!"-Programm analysieren.
Fügen Sie den Text für das Hello World-Programm als Konstante in Ihrer Program Klasse hinzu:
const string programText =
@"using System;
using System.Collections.Generic;
using System.Text;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(""Hello, World!"");
}
}
}";
Fügen Sie als Nächstes den folgenden Code hinzu, um die Syntaxstruktur für den Codetext in der programText Konstante zu erstellen. Fügen Sie die folgende Zeile zu Ihrer Main Methode hinzu:
SyntaxTree tree = CSharpSyntaxTree.ParseText(programText);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
Als Nächstes erstellen Sie einen CSharpCompilation aus dem Baum, den Sie erstellt haben. Das Beispiel „Hello World“ basiert auf den Typen String und Console. Sie müssen auf die Assembly verweisen, die diese beiden Typen in der Kompilierung deklariert. Fügen Sie der Main-Methode die folgende Zeile hinzu, um eine Kompilierung Ihres Syntaxbaums zu erstellen, einschließlich des Verweises auf die entsprechende Assembly.
var compilation = CSharpCompilation.Create("HelloWorld")
.AddReferences(MetadataReference.CreateFromFile(
typeof(string).Assembly.Location))
.AddSyntaxTrees(tree);
Die CSharpCompilation.AddReferences Methode fügt Verweise auf die Kompilierung hinzu. Die MetadataReference.CreateFromFile Methode lädt eine Assembly als Verweis.
Abfragen des semantischen Modells
Sobald Sie ein Compilation haben, können Sie bei ihm ein SemanticModel für jedes darin Compilation enthaltene SyntaxTree anfordern. Sie können sich das semantische Modell als Quelle für alle Informationen vorstellen, die Sie normalerweise von IntelliSense erhalten würden. Eine SemanticModel kann Fragen wie "Welche Namen befinden sich an diesem Speicherort?" beantworten, "Welche Member sind von dieser Methode aus zugänglich?", "Welche Variablen werden in diesem Textblock verwendet?", und "Worauf bezieht sich dieser Name/Ausdruck?" Fügen Sie diese Anmerkung hinzu, um das semantische Modell zu erstellen.
SemanticModel model = compilation.GetSemanticModel(tree);
Binden eines Namens
Die Compilation Erstellt die SemanticModel aus der SyntaxTree. Nach dem Erstellen des Modells können Sie es abfragen, um die erste using Direktive zu finden und die Symbolinformationen für den System Namespace abzurufen. Fügen Sie der Main Methode diese beiden Zeilen hinzu, um das semantische Modell zu erstellen und das Symbol für die erste using direktive abzurufen:
// Use the syntax tree to find "using System;"
UsingDirectiveSyntax usingSystem = root.Usings[0];
NameSyntax systemName = usingSystem.Name;
// Use the semantic model for symbol information:
SymbolInfo nameInfo = model.GetSymbolInfo(systemName);
Der vorangehende Code zeigt, wie der Name in der ersten using Direktive gebunden wird, um einen Microsoft.CodeAnalysis.SymbolInfo für den System Namespace abzurufen. Der vorangehende Code veranschaulicht außerdem, dass Sie das Syntaxmodell verwenden, um die Struktur des Codes zu finden; Sie verwenden das semantische Modell , um dessen Bedeutung zu verstehen. Das Syntaxmodell findet die Zeichenfolge System in der using Direktive. Das semantische Modell enthält alle Informationen zu den im System Namespace definierten Typen.
Aus dem SymbolInfo Objekt können Sie die Microsoft.CodeAnalysis.ISymbol unter Verwendung der SymbolInfo.Symbol Eigenschaft abrufen. Diese Eigenschaft gibt das Symbol zurück, auf das dieser Ausdruck verweist. Bei Ausdrücken, die sich nicht auf etwas beziehen (z. B. numerische Literale), lautet nulldiese Eigenschaft . Wenn der SymbolInfo.Symbol Wert nicht NULL ist, wird der ISymbol.Kind Typ des Symbols angegeben. In diesem Beispiel ist die Eigenschaft ISymbol.Kind ein SymbolKind.Namespace. Fügen Sie der Main-Methode den folgenden Code hinzu. Es ruft das Symbol für den System Namespace ab und zeigt dann alle im System Namespace deklarierten untergeordneten Namespaces an:
var systemSymbol = (INamespaceSymbol?)nameInfo.Symbol;
if (systemSymbol?.GetNamespaceMembers() is not null)
{
foreach (INamespaceSymbol ns in systemSymbol?.GetNamespaceMembers()!)
{
Console.WriteLine(ns);
}
}
Führen Sie das Programm aus, und die folgende Ausgabe sollte angezeigt werden:
System.Collections
System.Configuration
System.Deployment
System.Diagnostics
System.Globalization
System.IO
System.Numerics
System.Reflection
System.Resources
System.Runtime
System.Security
System.StubHelpers
System.Text
System.Threading
Press any key to continue . . .
Hinweis
Die Ausgabe enthält nicht jeden Namespace, der ein untergeordneter Namespace des System Namespaces ist. Es zeigt alle Namespaces an, die in dieser Kompilierung vorhanden sind und sich nur auf die Assembly beziehen, in der System.String deklariert ist. Alle in anderen Assemblys deklarierten Namespaces sind dieser Kompilierung nicht bekannt.
Binden eines Ausdrucks
Der vorangehende Code zeigt, wie Sie ein Symbol durch Binden an einen Namen finden. Es gibt andere Ausdrücke in einem C#-Programm, die gebunden werden können, die keine Namen sind. Um diese Funktion zu veranschaulichen, greifen wir auf die Bindung an ein einfaches Stringliteral zu.
Das Programm "Hello World" enthält eine Microsoft.CodeAnalysis.CSharp.Syntax.LiteralExpressionSyntax, die Zeichenfolge "Hello, World!", die auf der Konsole angezeigt wird.
Sie finden die Zeichenfolge "Hello, World!", indem Sie das einzelne Zeichenfolgenliteral im Programm suchen. Nachdem Sie den Syntaxknoten gefunden haben, rufen Sie dann die Typinformationen für diesen Knoten aus dem semantischen Modell ab. Fügen Sie der Main Methode den folgenden Code hinzu.
// Use the syntax model to find the literal string:
LiteralExpressionSyntax helloWorldString = root.DescendantNodes()
.OfType<LiteralExpressionSyntax>()
.Single();
// Use the semantic model for type information:
TypeInfo literalInfo = model.GetTypeInfo(helloWorldString);
Die Microsoft.CodeAnalysis.TypeInfo Struktur enthält eine TypeInfo.Type Eigenschaft, die den Zugriff auf die semantischen Informationen über den Typ des Literals ermöglicht. In diesem Beispiel ist dies der string Typ. Fügen Sie eine Deklaration hinzu, die diese Eigenschaft einer lokalen Variablen zuweist:
var stringTypeSymbol = (INamedTypeSymbol?)literalInfo.Type;
Um dieses Lernprogramm abzuschließen, lassen Sie uns eine LINQ-Abfrage erstellen, die eine Sequenz aller öffentlichen Methoden erzeugt, die für den string-Typ deklariert sind und ein string zurückgeben. Diese Abfrage wird komplex, daher bauen wir sie schrittweise Zeile für Zeile auf und fügen sie dann als einzelne Abfrage zusammen. Die Quelle für diese Abfrage ist die Abfolge aller Elemente, die für den string Typ deklariert sind:
var allMembers = stringTypeSymbol?.GetMembers();
Diese Quellsequenz enthält alle Elemente, einschließlich Eigenschaften und Felder, sodass sie mithilfe der ImmutableArray<T>.OfType Methode gefiltert werden, um Elemente zu finden, die Objekte sind Microsoft.CodeAnalysis.IMethodSymbol :
var methods = allMembers?.OfType<IMethodSymbol>();
Fügen Sie als Nächstes einen weiteren Filter hinzu, um nur die Methoden zurückzugeben, die öffentlich sind, und geben Sie folgendes stringzurück:
var publicStringReturningMethods = methods?
.Where(m => SymbolEqualityComparer.Default.Equals(m.ReturnType, stringTypeSymbol) &&
m.DeclaredAccessibility == Accessibility.Public);
Wählen Sie die Namenseigenschaft aus und stellen Sie sicher, dass nur unterschiedliche Namen angezeigt werden, indem Sie alle Überladungen entfernen.
var distinctMethods = publicStringReturningMethods?.Select(m => m.Name).Distinct();
Sie können die vollständige Abfrage auch mithilfe der LINQ-Abfragesyntax erstellen und dann alle Methodennamen in der Konsole anzeigen:
foreach (string name in (from method in stringTypeSymbol?
.GetMembers().OfType<IMethodSymbol>()
where SymbolEqualityComparer.Default.Equals(method.ReturnType, stringTypeSymbol) &&
method.DeclaredAccessibility == Accessibility.Public
select method.Name).Distinct())
{
Console.WriteLine(name);
}
Erstellen sie das Programm, und führen Sie es aus. Die folgende Ausgabe sollte angezeigt werden:
Join
Substring
Trim
TrimStart
TrimEnd
Normalize
PadLeft
PadRight
ToLower
ToLowerInvariant
ToUpper
ToUpperInvariant
ToString
Insert
Replace
Remove
Format
Copy
Concat
Intern
IsInterned
Press any key to continue . . .
Sie haben die Semantik-API verwendet, um Informationen zu den Symbolen zu finden und anzuzeigen, die Teil dieses Programms sind.