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 erkunden Sie die Syntax-API. Die Syntax-API bietet Zugriff auf die Datenstrukturen, die ein C#- oder Visual Basic-Programm beschreiben. Diese Datenstrukturen verfügen über genügend Details, sodass sie jedes Programm beliebiger Größe vollständig darstellen können. Diese Strukturen können vollständige Programme beschreiben, die ordnungsgemäß kompiliert und ausgeführt werden. Sie können auch unvollständige Programme beschreiben, während Sie sie schreiben, im Editor.
Um diesen umfangreichen Ausdruck zu ermöglichen, sind die Datenstrukturen und APIs, aus denen die Syntax-API besteht, notwendigerweise komplex. Beginnen wir mit dem Aussehen der Datenstruktur für das typische "Hello World"-Programm:
using System;
using System.Collections.Generic;
using System.Linq;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Sehen Sie sich den Text des vorherigen Programms an. Sie erkennen vertraute Elemente. Der gesamte Text stellt eine einzelne Quelldatei oder eine Kompilierungseinheit dar. Die ersten drei Zeilen dieser Quelldatei verwenden Direktiven. Die verbleibende Quelle ist in einer Namespacedeklaration enthalten. Die Namespacedeklaration enthält eine untergeordnete Klassendeklaration. Die Klassendeklaration enthält eine Methodendeklaration.
Die Syntax-API erstellt eine Baumstruktur mit dem Stamm, der die Kompilierungseinheit darstellt. Knoten im Baum stellen die using Direktiven, die Namespace-Deklaration und alle anderen Elemente des Programms dar. Die Baumstruktur wird bis zu den niedrigsten Ebenen weitergeführt: Die Zeichenfolge "Hello World!" ist ein Zeichenfolgenliteraltoken, das ein Nachkomme eines Arguments ist. Die Syntax-API bietet Zugriff auf die Struktur des Programms. Sie können bestimmte Codepraktiken abfragen, die gesamte Struktur durchlaufen, um den Code zu verstehen, und neue Strukturen erstellen, indem Sie die vorhandene Struktur ändern.
Diese kurze Beschreibung bietet eine Übersicht über die Art der Informationen, auf die mithilfe der Syntax-API zugegriffen werden kann. Die Syntax-API ist nichts anderes als eine formale API, die die vertrauten Codekonstrukte beschreibt, die Sie aus C# kennen. Die vollständigen Funktionen enthalten Informationen dazu, wie der Code formatiert ist, einschließlich Zeilenumbrüchen, Leerzeichen und Einzug. Mithilfe dieser Informationen können Sie den Code vollständig darstellen, wie er von menschlichen Programmierern oder dem Compiler geschrieben und gelesen wird. Mithilfe dieser Struktur können Sie mit dem Quellcode auf einer tief aussagekräftigen Ebene interagieren. Es handelt sich nicht mehr um Textzeichenfolgen, aber Daten, die die Struktur eines C#-Programms darstellen.
Zunächst müssen Sie 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 ".
Verständnis von Syntaxbäumen
Sie verwenden die Syntax-API für jede Analyse der Struktur von C#-Code. Die Syntax-API macht die Parser, die Syntaxstrukturen und Hilfsprogramme zum Analysieren und Erstellen von Syntaxstrukturen verfügbar. Hier erfahren Sie, wie Sie Code nach bestimmten Syntaxelementen durchsuchen oder den Code für ein Programm lesen.
Eine Syntaxstruktur ist eine Datenstruktur, die von den C#- und Visual Basic-Compilern verwendet wird, um C# und Visual Basic-Programme zu verstehen. Syntaxbäume werden von demselben Parser erstellt, der ausgeführt wird, wenn ein Projekt kompiliert wird oder ein Entwickler die F5-Taste drückt. Die Syntaxbäume weisen vollständige Treue zur Sprache auf; jede Information in einer Codedatei wird im Baum dargestellt. Durch das Schreiben eines Syntaxbaums in Text wird der exakt ursprüngliche Text reproduziert, der geparst wurde. Die Syntaxbäume sind ebenfalls unveränderlich; einmal erstellt eine Syntaxstruktur kann nie geändert werden. Verbraucher der Bäume können die Bäume auf mehreren Threads analysieren, ohne Sperren oder andere Parallelitätsmaßnahmen, da die Daten sich niemals ändern. Sie können APIs verwenden, um neue Strukturen zu erstellen, die das Ergebnis der Änderung einer vorhandenen Struktur sind.
Die vier wichtigsten Bausteine von Syntaxstrukturen sind:
- Die Microsoft.CodeAnalysis.SyntaxTree-Klasse, deren Instanz einen gesamten Parsetree darstellt. SyntaxTree ist eine abstrakte Klasse mit sprachspezifischen Ableitungen. Sie verwenden die Analysemethoden der Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree (oder Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree) Klasse, um Text in C# (oder Visual Basic) zu analysieren.
- Die Microsoft.CodeAnalysis.SyntaxNode Klasse, von deren Instanzen syntaktische Konstrukte wie Deklarationen, Anweisungen, Klauseln und Ausdrücke darstellen.
- Die Microsoft.CodeAnalysis.SyntaxToken Struktur, die ein einzelnes Schlüsselwort, einen Bezeichner, einen Operator oder eine Interpunktion darstellt.
- Und schließlich die Microsoft.CodeAnalysis.SyntaxTrivia Struktur, die syntaktisch unbedeutende Bits von Informationen wie den Leerzeichen zwischen den Tokens, Vorverarbeitungsanweisungen und Kommentaren darstellt.
Trivia, Token und Knoten werden hierarchisch zusammengesetzt, um eine Struktur zu bilden, die alles in einem Fragment von Visual Basic- oder C#-Code vollständig darstellt. Sie können diese Struktur mithilfe des Syntaxschnellansichtsfensters sehen. In Visual Studio Ansicht>Andere Fenster>Syntax Visualizer auswählen. Die vorangehende C#-Quelldatei, die mit der Syntaxschnellansicht untersucht wurde, sieht z. B. wie in der folgenden Abbildung aus:
SyntaxNode: Blau | SyntaxToken: Grün | SyntaxTrivia: Red 
Durch die Navigation innerhalb dieser Baumstruktur können Sie jede beliebige Anweisung, jeden Ausdruck, jedes Token oder jedes Leerzeichen in einer Codedatei finden.
Sie können zwar alles in einer Codedatei mithilfe der Syntax-APIs finden, die meisten Szenarien umfassen jedoch das Untersuchen kleiner Codeausschnitte oder das Suchen nach bestimmten Anweisungen oder Fragmenten. Die beiden folgenden Beispiele zeigen typische Verwendungsmöglichkeiten zum Durchsuchen der Codestruktur oder suchen nach einzelnen Anweisungen.
Baumtraversierung
Sie können die Knoten in einer Syntaxstruktur auf zwei Arten untersuchen. Sie können die Struktur durchlaufen, um jeden Knoten zu untersuchen, oder Sie können bestimmte Elemente oder Knoten abfragen.
Manuelles Durchlaufen
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 "SyntaxTreeManualTraversal", 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;
using System.Linq;
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();
Diese beiden Zeilen erstellen den Baum und rufen den Stammknoten dieses Baums ab. Sie können nun die Knoten im Baum untersuchen. Fügen Sie Ihrer Main Methode diese Zeilen hinzu, um einige der Eigenschaften des Stammknotens in der Baumstruktur anzuzeigen:
WriteLine($"The tree is a {root.Kind()} node.");
WriteLine($"The tree has {root.Members.Count} elements in it.");
WriteLine($"The tree has {root.Usings.Count} using directives. They are:");
foreach (UsingDirectiveSyntax element in root.Usings)
WriteLine($"\t{element.Name}");
Führen Sie die Anwendung aus, um zu sehen, was Ihr Code über den Stammknoten in dieser Struktur ermittelt hat.
Normalerweise durchlaufen Sie den Baum, um mehr über den Code zu erfahren. In diesem Beispiel analysieren Sie Code, den Sie kennen, um die APIs zu erkunden. Fügen Sie den folgenden Code hinzu, um das erste Element des root Knotens zu untersuchen:
MemberDeclarationSyntax firstMember = root.Members[0];
WriteLine($"The first member is a {firstMember.Kind()}.");
var helloWorldDeclaration = (NamespaceDeclarationSyntax)firstMember;
Dieses Mitglied ist ein Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax. Sie stellt alles im Bereich der namespace HelloWorld Deklaration dar. Fügen Sie den folgenden Code hinzu, um zu untersuchen, welche Knoten innerhalb des HelloWorld Namespace deklariert werden:
WriteLine($"There are {helloWorldDeclaration.Members.Count} members declared in this namespace.");
WriteLine($"The first member is a {helloWorldDeclaration.Members[0].Kind()}.");
Führen Sie das Programm aus, um zu sehen, was Sie gelernt haben.
Nachdem Sie nun wissen, dass die Deklaration ein Microsoft.CodeAnalysis.CSharp.Syntax.ClassDeclarationSyntaxist, deklarieren Sie eine neue Variable dieses Typs, um die Klassendeklaration zu untersuchen. Diese Klasse enthält nur ein Element: die Main Methode. Fügen Sie den folgenden Code hinzu, um die Main Methode zu finden, und wandeln Sie sie in eine Microsoft.CodeAnalysis.CSharp.Syntax.MethodDeclarationSyntax.
var programDeclaration = (ClassDeclarationSyntax)helloWorldDeclaration.Members[0];
WriteLine($"There are {programDeclaration.Members.Count} members declared in the {programDeclaration.Identifier} class.");
WriteLine($"The first member is a {programDeclaration.Members[0].Kind()}.");
var mainDeclaration = (MethodDeclarationSyntax)programDeclaration.Members[0];
Der Methodendeklarationsknoten enthält alle syntaktischen Informationen zur Methode. Lassen Sie uns den Rückgabetyp der Main Methode, die Anzahl und die Typen der Argumente und den Textkörper der Methode anzeigen. Fügen Sie den folgenden Code hinzu:
WriteLine($"The return type of the {mainDeclaration.Identifier} method is {mainDeclaration.ReturnType}.");
WriteLine($"The method has {mainDeclaration.ParameterList.Parameters.Count} parameters.");
foreach (ParameterSyntax item in mainDeclaration.ParameterList.Parameters)
WriteLine($"The type of the {item.Identifier} parameter is {item.Type}.");
WriteLine($"The body text of the {mainDeclaration.Identifier} method follows:");
WriteLine(mainDeclaration.Body?.ToFullString());
var argsParameter = mainDeclaration.ParameterList.Parameters[0];
Führen Sie das Programm aus, um alle Informationen anzuzeigen, die Sie zu diesem Programm entdeckt haben:
The tree is a CompilationUnit node.
The tree has 1 elements in it.
The tree has 4 using directives. They are:
System
System.Collections
System.Linq
System.Text
The first member is a NamespaceDeclaration.
There are 1 members declared in this namespace.
The first member is a ClassDeclaration.
There are 1 members declared in the Program class.
The first member is a MethodDeclaration.
The return type of the Main method is void.
The method has 1 parameters.
The type of the args parameter is string[].
The body text of the Main method follows:
{
Console.WriteLine("Hello, World!");
}
Abfragemethoden
Zusätzlich zum Durchlaufen von Strukturen können Sie auch den Syntaxbaum mithilfe der Abfragemethoden untersuchen, die für Microsoft.CodeAnalysis.SyntaxNode definiert sind. Diese Methoden sollten jedem, der mit XPath vertraut ist, sofort vertraut sein. Sie können diese Methoden mit LINQ verwenden, um Elemente in einer Baumstruktur schnell zu finden. Der SyntaxNode hat Abfragemethoden wie DescendantNodes, AncestorsAndSelf und ChildNodes.
Sie können diese Abfrageverfahren verwenden, um das Argument für die Main Methode als Alternative zum Navigieren im Baum zu finden. Fügen Sie am Ende der Main Methode den folgenden Code hinzu:
var firstParameters = from methodDeclaration in root.DescendantNodes()
.OfType<MethodDeclarationSyntax>()
where methodDeclaration.Identifier.ValueText == "Main"
select methodDeclaration.ParameterList.Parameters.First();
var argsParameter2 = firstParameters.Single();
WriteLine(argsParameter == argsParameter2);
Die erste Anweisung verwendet einen LINQ-Ausdruck und die DescendantNodes Methode, um denselben Parameter wie im vorherigen Beispiel zu finden.
Führen Sie das Programm aus, und Sie können sehen, dass der LINQ-Ausdruck denselben Parameter wie die manuelle Navigation im Baum gefunden hat.
Im Beispiel werden WriteLine Anweisungen verwendet, um Informationen über die Syntaxbäume anzuzeigen, während diese durchlaufen werden. Sie können auch viel mehr erfahren, indem Sie das fertige Programm unter dem Debugger ausführen. Sie können mehr der Eigenschaften und Methoden untersuchen, die Teil der Syntaxstruktur sind, die für das Hello World-Programm erstellt wurde.
Syntax-Läufer
Häufig möchten Sie alle Knoten eines bestimmten Typs in einer Syntaxstruktur finden, z. B. jede Eigenschaftsdeklaration in einer Datei. Indem Sie die Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker Klasse erweitern und die VisitPropertyDeclaration(PropertyDeclarationSyntax) Methode außer Kraft setzen, verarbeiten Sie jede Eigenschaftsdeklaration in einer Syntaxstruktur, ohne vorher die Struktur zu kennen. CSharpSyntaxWalker ist eine spezielle Art von CSharpSyntaxVisitor, die rekursiv einen Knoten und jedes seiner untergeordneten Elemente besucht.
In diesem Beispiel wird ein CSharpSyntaxWalker implementiert, das einen Syntaxbaum untersucht. Es sammelt using Direktiven, die keinen System-Namespace importieren.
Erstellen Sie ein neues C# -Stand-Alone Codeanalysetoolprojekt ; nennen Sie es "SyntaxWalker".
Sie können den fertigen Code für dieses Beispiel in unserem GitHub-Repository sehen. Das Beispiel auf GitHub enthält beide Projekte, die in diesem Lernprogramm beschrieben werden.
Wie im vorherigen Beispiel können Sie eine Zeichenfolgenkonstante definieren, um den Text des zu analysierenden Programms zu speichern:
const string programText =
@"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
namespace TopLevel
{
using Microsoft;
using System.ComponentModel;
namespace Child1
{
using Microsoft.Win32;
using System.Runtime.InteropServices;
class Foo { }
}
namespace Child2
{
using System.CodeDom;
using Microsoft.CSharp;
class Bar { }
}
}";
Dieser Quelltext enthält using-Direktiven, die auf vier verschiedenen Ebenen verteilt sind: auf der Dateiebene, im obersten Namespace und in den zwei geschachtelten Namespaces. In diesem Beispiel wird ein Kernszenario für die Verwendung der CSharpSyntaxWalker Klasse zum Abfragen von Code hervorgehoben. Es wäre umständlich, jeden Knoten in der Stammsyntaxstruktur zu besuchen, um mithilfe von Deklarationen zu suchen. Stattdessen erstellen Sie eine abgeleitete Klasse und überschreiben die Methode, die nur aufgerufen wird, wenn der aktuelle Knoten in der Struktur eine using Direktive ist. Ihr Besucher arbeitet nicht an anderen Knotentypen. Diese einzelne Methode untersucht jede der using Direktiven und erstellt eine Auflistung der Namespaces, die sich nicht im System-Namespace befinden. Sie erstellen eine CSharpSyntaxWalker Richtlinie, die alle using Direktiven, aber nur die using Direktiven untersucht.
Nachdem Sie den Programmtext definiert haben, müssen Sie eine SyntaxTree Struktur erstellen und den Stammknoten erhalten:
SyntaxTree tree = CSharpSyntaxTree.ParseText(programText);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
Erstellen Sie als Nächstes eine neue Klasse. In Visual Studio wählen Sie Projekt>Neues Element hinzufügen aus. Geben Sie im Dialogfeld " Neues Element hinzufügen " UsingCollector.cs als Dateinamen ein.
Sie implementieren die using Besucherfunktionalität in der UsingCollector Klasse. Beginnen Sie, indem Sie die UsingCollector Klasse ableiten von CSharpSyntaxWalker.
class UsingCollector : CSharpSyntaxWalker
Sie benötigen Speicher, um die Namespaceknoten zu speichern, die Sie sammeln. Deklarieren Sie eine öffentliche, schreibgeschützte Eigenschaft in der UsingCollector-Klasse. Sie verwenden diese Eigenschaft, um die gefundenen UsingDirectiveSyntax-Knoten zu speichern.
public ICollection<UsingDirectiveSyntax> Usings { get; } = new List<UsingDirectiveSyntax>();
Die Basisklasse implementiert die Logik, CSharpSyntaxWalker um jeden Knoten in der Syntaxstruktur zu besuchen. Die abgeleitete Klasse setzt die Methoden außer Kraft, die für die spezifischen Knoten aufgerufen werden, an denen Sie interessiert sind. In diesem Fall interessieren Sie sich für jede using Direktive. Das bedeutet, dass Sie die VisitUsingDirective(UsingDirectiveSyntax) Methode überschreiben müssen. Das ein Argument für diese Methode ist ein Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax Objekt. Das ist ein wichtiger Vorteil für die Verwendung der Besucher: Sie rufen die überschriebenen Methoden mit Argumenten auf, die bereits in den spezifischen Knotentyp umgestellt wurden. Die Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax Klasse verfügt über eine Name Eigenschaft, die den Namen des zu importierenden Namespaces speichert. Es handelt sich um eine Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax. Fügen Sie den folgenden Code in das VisitUsingDirective(UsingDirectiveSyntax) Override hinzu:
public override void VisitUsingDirective(UsingDirectiveSyntax node)
{
WriteLine($"\tVisitUsingDirective called with {node.Name}.");
if (node.Name.ToString() != "System" &&
!node.Name.ToString().StartsWith("System."))
{
WriteLine($"\t\tSuccess. Adding {node.Name}.");
this.Usings.Add(node);
}
}
Wie im vorherigen Beispiel haben Sie eine Vielzahl von WriteLine Anweisungen hinzugefügt, um das Verständnis dieser Methode zu unterstützen. Sie können sehen, wann sie aufgerufen wird und welche Argumente jedes Mal an sie übergeben werden.
Schließlich müssen Sie zwei Codezeilen hinzufügen, um den UsingCollector aufzurufen und damit den Stammknoten zu besuchen und alle using-Direktiven zu sammeln. Fügen Sie dann eine foreach Schleife hinzu, um alle using Direktiven, die Ihr Sammler gefunden hat, anzuzeigen.
var collector = new UsingCollector();
collector.Visit(root);
foreach (var directive in collector.Usings)
{
WriteLine(directive.Name);
}
Kompilieren sie das Programm, und führen Sie es aus. Die folgende Ausgabe sollte angezeigt werden:
VisitUsingDirective called with System.
VisitUsingDirective called with System.Collections.Generic.
VisitUsingDirective called with System.Linq.
VisitUsingDirective called with System.Text.
VisitUsingDirective called with Microsoft.CodeAnalysis.
Success. Adding Microsoft.CodeAnalysis.
VisitUsingDirective called with Microsoft.CodeAnalysis.CSharp.
Success. Adding Microsoft.CodeAnalysis.CSharp.
VisitUsingDirective called with Microsoft.
Success. Adding Microsoft.
VisitUsingDirective called with System.ComponentModel.
VisitUsingDirective called with Microsoft.Win32.
Success. Adding Microsoft.Win32.
VisitUsingDirective called with System.Runtime.InteropServices.
VisitUsingDirective called with System.CodeDom.
VisitUsingDirective called with Microsoft.CSharp.
Success. Adding Microsoft.CSharp.
Microsoft.CodeAnalysis
Microsoft.CodeAnalysis.CSharp
Microsoft
Microsoft.Win32
Microsoft.CSharp
Press any key to continue . . .
Glückwunsch! Sie haben die Syntax-API verwendet, um bestimmte Arten von Direktiven und Deklarationen im C#-Quellcode zu finden.