Die kommenden Generationen von C# und Visual Basic

Wanted Roslyn
Kommentare

Unter dem Codenamen „Roslyn“ arbeitet Microsoft derzeit an einer neuartigen Compilertechnologie, bei der die wichtigsten Funktionalitäten, die aus Quellcode eine ausführbare Assembly machen, über ein API offengelegt werden. Seit November letzten Jahres gibt es eine erste CTP dieser vielversprechenden Neuerung, die hier vorgestellt wird.

Jetzt hat es also auch den Compiler erwischt: Neben CRMs, Logistiklösungen und persönlichen Zeitabrechnungsprogrammen soll nach den Plänen der Microsoft-Entwicklungsabteilung in Zukunft auch der ehrwürdige Compiler als „Service“ angeboten werden. Kannten Entwicklerveteranen den Compiler in der Vergangenheit unter unscheinbaren Namen wie C2.exe und war sein Innenleben lediglich einer kleinen Gruppe von Insidern vertraut, steht seine Funktionalität in Zukunft über ein umfangreiches Klassen-API allen zur Verfügung. Der strategische Hintergrund ist offensichtlich: Nachdem bei Microsoft bereits der Team Foundation Server in der Cloud läuft und damit in Zukunft ganze Build-Prozesse unter Azure erledigt werden können, muss auch das Kompilieren von Quellcode „in der Cloud“ möglich sein. Die Grundlage dafür bietet eine neue Klassenbibliothek, an der bei Microsoft bereits seit einigen Jahren unter dem Codenamen Roslyn (benannt nach einem Ort in der Nähe von Redmond, in dem vermutlich Chefentwickler Anders Hejlsberg ein Wochenendhaus besitzt) gearbeitet wird. Erstmals wurde Roslyn von Anders Hejlsberg unter dem Schlagwort „Compiler as a Service“ auf der PDC 2008 in seiner Session über die Zukunft von C# (bezogen auf die damals aktuelle Version 3.0) vorgestellt [1]. Seit November 2011 gibt es eine erste CTP, die zwar noch alles andere als vollständig ist, aber bereits einen sehr guten Eindruck von den Möglichkeiten vermittelt, die eines schönen Tages allen Entwicklern zur Verfügung stehen werden. Vor allem die zahlreichen Samples (sowohl in C# als auch in Visual Basic) geben einen sehr guten Überblick über die neuen Möglichkeiten. Programmcode unter Azure in der Cloud kompilieren zu können, ist aber nur eines von vielen Einsatzgebieten und aktuell eher ein Randaspekt. Sehr viel wichtiger sind ganz profane Dinge, wie den Quellcode eines C#- oder Visual-Basic-Programms über ein API ansprechen zu können. Damit lassen sich Visual-Studio-Erweiterungen für die Codeanalyse oder Umgestaltung deutlich einfacher und weitreichender entwickeln als dies mit den aktuellen technischen Voraussetzungen der Fall ist.

CodeDOM vs. Roslyn

Die Möglichkeit aus Quellcode während der Programmausführung ausführbaren IL-Code zu machen, bietet das .NET Framework seit der Version 2.0. Beim .NET Framework ist der Compiler keine monolithische Exe-Datei, sondern basiert auf Klassen im Namespace System.CodeDom.Compiler. Im einfachsten Fall kompilieren zwei Befehlszeilen eine Quelltextdatei in eine Exe-Datei:

CodeDomProvider codeProv = CodeDomProvider.CreateProvider("CSharp");
CompilerResults res = codeProv.CompileAssemblyFromSource(paras, source  

Die logische Struktur des Quellcodes kann über ein Document Object Model (CodeDOM) nicht nur angesprochen, sondern auch aufgebaut werden, wobei bei Weitem nicht alle Sprachelemente von C# unterstützt werden. Roslyn geht einen großen Schritt weiter, indem zum einen eine vollständige Unterstützung aller Sprachelemente der aktuellen Versionen von C# und VB angestrebt wird und zum anderen, neben einem rein syntaktischen Modell, das nur die Sprachelemente enthält, auch ein semantisches Modell angeboten wird, bei dem die Bedeutung der Sprachelemente eine Rolle spielt.

Installation und erste Schritte

Die Roslyn-CTP ist schnell installiert. Voraussetzung sind Visual Studio 2010 SP1 und das Visual Studio 2010 SDK. Nach der Installation steht ein Satz von fünf Projektvorlagen für C# und Visual Basic zur Verfügung (Abb. 1). Bei den Express-Editionen werden die Vorlagen leider nicht angeboten, hier müssen die benötigten Roslyn Assemblies, wie RoslynCompilers.dll und Roslyn.Compilers.CSharp.dll direkt eingebunden werden. Wer sich zunächst nur einen Überblick über die Möglichkeiten verschaffen möchte, sollte die Samples im Verzeichnis %userprofile%Microsoft Codename Roslyn CTP – October 2011 der Reihe nach ausprobieren. Es lohnt sich, denn die Beispielprojekte stellen einen sehr guten Querschnitt über die vielfältigen Anwendungsmöglichkeiten der aktuellen Roslyn-CTP dar. Besonders interessant sind zwei „Sprachkonverter“, die C#- in Visual-Basic-Code und umgekehrt konvertieren (klar, die Open-Source-IDE Sharp Develop konnte das schon immer, aber dahinter stand kein umfangreiches API, das jeder Entwickler für eigene Konverter hätte nutzen können). Nett ist z. B. auch das Beispielprojekt ImplementNotifyPropertyChangedCS, das als Visual-Studio-Erweiterung nach der Installation in einer Klasse nicht nur die Schnittstelle INotifyPropertyChanged implementiert, sondern auch in jeder Property den Aufruf des OnChanged-Events einfügt. Auch diese Art von Komfort gibt es bereits seit Langem, aber mit Roslyn stehen diese Möglichkeiten zur Codeumgestaltung auf eine einfache und vor allem bestens dokumentierte Art und Weise für alle zur Verfügung.

Abb. 1: Nach der Installation von Roslyn stehen neue Projektvorlagen zur Auswahl
Abb. 1: Nach der Installation von Roslyn stehen neue Projektvorlagen zur Auswahl
Ein Hallo Welt für Roslyn

Wie einfach es ist, die ersten Schritte in der neuen Welt der Syntaxbäume zu gehen, macht eine kleine Übung deutlich. Sie setzt lediglich voraus, dass die Roslyn-CTP installiert wurde. Starten Sie Visual Studio oder Visual C# 2010 Express, legen Sie eine Konsolenanwendung an und fügen Sie Verweise auf Roslyn.Compilers.dll und Roslyn.Compilers.CSharp.dll ein. Fügen Sie eine Klasse Person hinzu, die eine Reihe beliebiger Properties erhält. Im Folgenden soll für diese Klasse ein Syntaxbaum erstellt werden. Laden Sie den Inhalt der Quelltextdatei mit der Klassendefinition über einen StreamReader und weisen Sie den Inhalt einer String-Variablen, z. B. code, zu. Fügen Sie den folgenden Befehl ein, der den Syntaxbaum anlegt:

SyntaxTree tree = SyntaxTree.ParseCompilationUnit(code);  

Fügen Sie einen weiteren Befehl ein, der das Stammelement des Baumes anspricht:

var root = (CompilationUnitSyntax)tree.Root;  

Mehr ist nicht zu tun. Über die Methode DescendentNodes() werden alle Baumknoten angesprochen, die Methode OfType () wählt nur jene Knoten des Typs T aus. Sollen z. B. nur die „Statement-Deklarationen“ aufgelistet werden, erledigen dies die folgenden Befehle:

var StateList = root.DescendentNodes()
                    .OfType();  

Schauen Sie sich die verschiedenen Syntaxtypen im Objektbrowser an – Sie kommen auf diese Weise schnell dahinter, welche Sprachelemente sich über den Syntaxbaum ansprechen lassen. Listing 1 fasst die vorgestellte Befehlsfolge noch einmal zusammen.

Syntaxbaum und semantischer Baum

Die Kompilierung eines Quelltextes ist bekanntlich ein mehrstufiger Prozess. Im ersten Schritt wird der Quelltext geparst und dabei in Tokens (Symbolnamen) zerlegt. Im nächsten Schritt werden aus den Deklarationen im Quelltext und zusätzlichen Metadaten (Attribute) benannte Symbole gebildet. Daran schließt sich die „Bind-Phase“ an, in der die Bezeichner im Quellcode den Symbolen zugeordnet werden Am Ende folgt das „Emittieren“ des IL-Codes in Gestalt einer Assembly. Mit jeder Phase ist bei Roslyn ein Objektmodell verbunden, das einen Zugriff auf die Elemente erlaubt, die aus der abgeschlossenen Phase resultieren. Es stehen daher verschiedene APIs zur Verfügung:

  • Compiler-API
  • Scripting API
  • Workspace API
  • Services API

Richtig interessant sind nur Compiler- und Scripting APIs. Der wichtigste Aspekt, der sich aus den Compiler-APIs ergibt, ist die Möglichkeit den Syntaxbaum (engl. syntax tree) anzusprechen, der aus dem Parsen des Quelltextes resultiert. Der Syntaxbaum enthält unter anderem den kompletten Quelltext in einer Baumstruktur – und zwar exakt in jener Form, in der er eingegeben wurde (also auch inklusive der Leerzeichen). Damit lässt sich über den Syntaxbaum auch der Originalquelltext abfragen. Die einzelnen Programmelemente des Quelltextes stehen als Knoten des Syntaxbaums zur Verfügung. Da einzelne Knoten gemäß der Bedeutung der Programmelemente, die sie repräsentieren, eine unterschiedliche Bedeutung besitzen, sind sie typisiert, d. h. für jedes Programmelement (Deklaration, Ausdruck, Befehl usw.) existiert ein eigener Typ.

Listing 1: Auflisten der Properties eines Quellcodebereichs

using (StreamReader sr = new StreamReader(CodePfad)) {
  string code = sr.ReadToEnd();
  SyntaxTree tree = SyntaxTree.ParseCompilationUnit(code);
  var root = (CompilationUnitSyntax)tree.Root;
  // Alle Deklarationen innerhalb der Typ-Definition
  var ExpList1 = root.DescendentNodes()
    .OfType();
  var ExpList2 = root.DescendentNodes()
    .OfType();
}  

Neben der Syntaxebene besitzt der Quellcode auch eine semantische Ebene. Hier geht es unter anderem um Fragen wie welche Variablen von einer bestimmten Befehlszeile aus sichtbar sind oder welcher Typ aus einem Ausdruck resultiert. Die Befehlsfolge in Listing 2 kompiliert den Quelltext, der über den Syntaxbaum in Gestalt der Variablen tree zur Verfügung gestellt wird, und gibt alle Members der Klasse App mit ihrem Namen und Typ aus.

Listing 2: Zugriff auf den semantischen Baum

var comp = Compilation.Create("HalloRosi")
  .AddReferences(new AssemblyFileRefer-ence(typeof(object).Assembly.Location))
  .AddSyntaxTrees(tree);
  NamedTypeSymbol nts = comp.GetTypeByMetadataName("App");
  foreach (Symbol member in nts.GetMembers())
    Console.WriteLine("Name: {0}, Typ: {1}", member.Name, member.Kind);  
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -