Aus Linux-Magazin 11/2008

C#-Entwicklung unter Linux

© Aleksei Vasileika, Fotolia.com

C# knüpft nach Meinung vieler erfolgreich an die Fortschritte von Java gegenüber C++ an. Mit der kommenden Mono-2.0-Release schließt der freie Port der Dotnet-Umgebung zum C#-Standard 3.0 auf, sodass die Sprache auch unter Linux zur interessanten C++- und Java-Alternative wird.

Mit der Combo aus Dotnet-Plattform, der Programmiersprache C# und der IDE Visual Studio hat Microsoft ohne Zweifel eine fortschrittliche Programmierumgebung geschaffen. Mit der bevorstehenden Version 2.0 von Mono trifft Dotnet – nicht ganz ohne Zutun der Redmonder – auf ernsthafte Unterstützung aus dem Linux- und Open-Source-Lager. Das Linux-Magazin führt mit einer vierteiligen Serie in die Programmierung der nun auch in einer leistungsfähigen Linux-Version verfügbaren Sprache ein.

Als Grund für die Beliebtheit von C# unter Windows führen Anhänger an, dass Microsoft aus den Fehlern der Vergangenheit, etwa bei C++ oder Java, gelernt hat (siehe Kasten “Von C bis C#”).

Von C bis C#

C ist ein Spross der Unix-Entwicklung, der dem Versuch, das Betriebssystem unabhängiger von der Hardware zu machen, entsprang. Als rein prozedurale Sprache brachte C jedoch viele Einschränkungen mit sich. Objektorientierte Programmiersprachen wie Simula und Smalltalk zeigten eine Lösung auf, indem sie Daten und Methoden in unabhängigen Einheiten, den Klassen, kapselten. C++ versucht diese Konzepte in die C-Welt zu übertragen. Zumindest an der Zahl der verfügbaren Publikationen gemessen kommt gegenwärtig aber Java der Status des beliebtesten C-Ablegers zu.

Java führt neben der architekturbedingten Portabilität eine Vielzahl fortgeschrittener Sprachkonzepte wie beispielsweise einen Garbage Collector ein. Microsofts eigener Java-Implementierung Visual J++ war jedoch wegen eines Rechtstreits mit Sun nur ein kurzes Leben beschieden. Stattdessen meldeten sich die Redmonder mit der Dotnet-Plattform [1] und deren Hauptsprache C# zu Wort. Zusammen mit Intel und Hewlett-Packard hat Microsoft C# bereits im Jahr 2000 bei der ECMA zur Standardisierung angemeldet. Aktuell gültig ist Version 3.0 des Sprachstandards.

Gilt Java manchen als das bessere C++, so erscheinen viele Konzepte von Dotnet und C# wie eine Erweiterung der Ideen von Sun. Das Konzept der Virtual Maschine, die in Dotnet Common Language Runtime heißt, ermöglicht hier das Zusammenspiel verschiedener Programmiersprachen. C#-Bibliotheken sind zum Beispiel mit kleinen Einschränkungen auch von Visual Basic aus nutzbar und umgekehrt.

C# bietet außerdem einen im Vergleich zu Java noch einmal erweiterten Sprachumfang. Anders als Java erlaubt es einen direkten Zugriff auf Speicheradressen und unterstützt Pointer in C++-Manier, allerdings nur in explizit als »unsafe« markiertem Code. Auf private Variablen greifen Entwickler dank »get()«- und »set()«-Methoden einfacher zu und können dabei noch beliebigen Code ausführen. Ein neues Feature, das die künftige Mono-2.0-Release bereits unterstützt, ist die Language Integrated Query (LINQ), die den Datenzugriff mit SQL-ähnlicher Syntax erlaubt.

Trendwende

Die bevorstehende Mono-Release 2.0 [2] überwindet die Plattformbarriere: Laut den Draft Release Notes [3] unterstützt Mono nun den vollen Sprachumfang des C#-3.0-Standards. Gegenüber früheren Versionen – das Projekt startete bereits 2001 – hat die Praxistauglichkeit unter Linux große Fortschritte gemacht.

Außerdem laufen unter der mit Mono implementierten Common Language Runtime wie in Microsofts eigener Version nicht nur C#-Programme. Die virtuelle Maschine unterstützt ein gutes Dutzend weiterer Programmiersprachen [4]. C# ist zudem auch in der Lage, nativen C++-Code einzubinden. So lassen sich Programme mit hoher Performance schreiben, die trotzdem in weiten Teilen des Code auf den Komfort von C# zurückgreifen.

Grafische Qualitäten

Anbindungen an Gnome und, leider deutlich weniger entwickelt, an QT/KDE erlauben es, nun auch unter Linux mit C# grafische Programme zu schreiben. C++- oder Java-Programmierer lernen die C#-Syntax innerhalb weniger Tage. Die wohl wichtigste Einschränkung bleibt, dass nicht alle unter Windows verfügbaren Dotnet-Bibliotheken für Mono existieren. In manchen Fällen dürfte es wegen der zügigen Entwicklung von Microsofts Dotnet-Plattform oder lizenzrechtlicher Probleme auch schwerfallen, die Lücken zu schließen. Hier ist die Open-Source-Community gefragt, die Mono-Entwickler bei ihrer Arbeit zu unterstützen. Wie beeindruckend das bereits Erreichte aller Einschränkungen zum Trotz ist, zeigt die Mono-Dokumentation [5].

Achtung, Gefahr!

Laut Mono-FAQ [6] bestanden zumindest bei der Implementierung einer Open-Source-Version von Microsofts Silverlight – einem Konkurrenzprodukt zu Adobe Flash – direkte Kontakte zwischen dem Mono-Sponsor Novell und Microsoft. Diese Tatsache dürfte neben der Herkunft der Sprache manchen eingefleischten Open-Source-Befürworter nachdenklich stimmen. Novell war ja selber in jüngerer Vergangenheit für sein Patentübereinkommen mit Microsoft in die Kritik geraten.

Hinter Mono steht andererseits niemand anderes als Miguel de Icaza. Der hat schon mit der Positionierung von Gnome gegen KDE bewiesen, dass er, wenn es um den Open-Source-Gedanken geht, zu keinen Kompromissen bereit ist. Wer Open-Source-Konferenzen besucht, kann zudem bestätigen, dass sich Microsoft verstärkt um Kontakte zur Open-Source-Community bemüht, wie auch das Engagement der Redmonder in der Open Source Business Foundation (OSBF) zeigt. Ausgerechnet den Bereich Interoperabilität adressiert Microsoft hier. Aus Open-Source-Sicht ist Microsofts Verbindung zu Mono und C# also sicher ein Grund für verstärkte Aufmerksamkeit, aber kaum zur Sorge.

Setup

Pakete einer aktuellen Mono-Version, deren Versionsnummer der 2 so nahe wie möglich kommen sollte, sind in den meisten neuen Distributionen enthalten. Unter Open Suse 11 ist Mono in der Version 1.9.1 verfügbar. Sie lässt sich einfach über Yast nachinstallieren, sofern sie nicht sowieso schon auf der Festplatte liegt. Die Befehle »gmcs –version« und »mono –version« fragen die Versionsnummer ab.

Übersetzer

Gmcs ist der C#-Compiler, der Generics, unterstützt. Dies ist die C#-Entsprechung für Templates in C++. Mono ist die Laufzeitumgebung, die zur Ausführung übersetzter Programme nötig ist. Unter [7] ist ein Binary-Installer der Mono-2.0-Preview-2-Version verfügbar. Für die Einführung in diesem ersten Artikel genügt jedoch Version 1.9.1. Wer Schwierigkeiten hat, diese auf seinem System zum Laufen zu bekommen, findet auf der Mono-Webseite Images mit Version 1.9.1 für den VMware-Player.

Handwerkszeug

Wer Programmcode schreibt, kann auf einen Editor mit Syntax-Highlighting kaum verzichten. Für C# gibt es einen Emacs-Mode (Abbildung 1, [8]). Wem das nicht genügt, der findet in Monodevelop [9] eine Open-Source-Entwicklungsumgebung. Fürs erste Hello-World-Programm (Listing 1) reicht ein Editor aber aus.

Abbildung 1: Neue Aufgabe für den Veteranen: Ein frei verfügbarer Mode macht Emacs C#-tauglich.

Abbildung 1: Neue Aufgabe für den Veteranen: Ein frei verfügbarer Mode macht Emacs C#-tauglich.

Das Programm sieht auf den ersten Blick einem C++- oder Java-Programm sehr ähnlich. Aufgrund der gemeinsamen Ahnenreihe ist dies natürlich kein Zufall. Sie zeigt sich zum Beispiel darin, dass die beiden Kommentarformen »/* */« und »//« möglich sind. Ferner gibt es Dokumentationskommentare [10].

Listing 1:
»helloWorld.cs«

01 using System;
02 
03 namespace LinuxMagazinHelloWorld
04 {
05   class HalloWelt
06   {
07     public static void Main(string[] args)
08     {
09       for(int i=0; i<2; i++){
10         // Ohne "using System;" muessten wir hier
11         // System.Console.WriteLine("..."); schreiben
12         Console.WriteLine("Hallo Nr. {0}, Linux Magazin!", i.ToString());
13       }
14     }
15   }
16 }

Wie in C++ stellt eine Methode »Main()« den Startpunkt des Programms dar. Allerdings schreibt sie sich in C# groß und ist stets in eine Klasse (im Beispiel in »HalloWelt«) eingebettet. Entsprechend ihrer besonderen Funktion ist die Methode als »static« zu deklarieren.

Anders als C++ ist C# zwingend objektorientiert aufgebaut. C++ nötigt den Entwickler nicht dazu, überhaupt Klassen zu verwenden. Dagegen leiten sich alle Klassen der Dotnet-Klassenhierarchie von der Klasse »System.Object« ab. Zeile 12 in der »for()«-Schleife kann daher für die Variable »i« die von der Klasse »System.Object« vererbte Methode »ToString()« aufrufen. Ihr Rückgabewert ersetzt den Ausdruck »{0}« im Text, den »Konsole.WriteLine()« ausgibt.

Die explizite Typenumwandlung in Zeile 12 ist streng genommen nicht nötig, die Zeile hätte auch »Console.WriteLine(“Hallo Nr. {0}, Linux Magazin!”, i);« lauten können. Das Weglassen des Semikolons am Ende der Klassendeklaration (Zeile 16) ist übrigens kein Versehen, in C# ist dies die Regel.

Das Beispiel aus Listing 1 lässt sich auf der Kommandozeile mit dem Befehl »gmcs helloWorld.cs« übersetzen. Das Ergebnis ist eine Datei »helloWorld.exe«. Unter Linux ist zu deren Ablauf die Mono-Runtime-Umgebung nötig. Erst der Aufruf »mono helloWorld.exe« startet daher das Programm:

Hallo Nr. 0, Linux Magazin!
Hallo Nr. 1, Linux Magazin!

Es gibt – wie erwartet – den String aus Zeile 12 zweimal auf der Kommandozeile aus und ersetzt dabei »{0}« durch den Schleifenzähler.

Weitergesponnen

Listing 2 zeigt, wie C# mit Objekten umgeht. Die Klasse »ObjectPrinter« erzeugt ein Array, das Instanzen der Klassen »Apfel« und »Birne« enthält. Eine »print()«-Methode soll Informationen zu den einfachen Testobjekten ausgeben. Entscheidend für das Verständnis des Code ist, dass sich C#-Klassen wie alle im Sprachumfang enthaltenen Objekte implizit von »System.Object« ableiten. Daher kann der Entwickler in C# ein Array von »Object«-Objekten erzeugen (Zeile 34) und ihm wie in Smalltalk eigene Klasseninstanzen zuweisen (Zeilen 21 und 22).

Listing 2:
»objectCollection.cs«

01 using System;
02 
03 namespace LinuxMagazineObjectPrinter
04 {
05   class Apfel {
06     public Apfel()
07     { Console.WriteLine("Im Apfel-Konstruktor: {0}", secret_); }
08 
09     private string secret_ = "Ich bin ein Apfel";
10   }
11 
12   class Birne {
13     public Birne()
14     { Console.WriteLine("Im Birnen-Konstruktor: {0}", secret_); }
15 
16     private string secret_ = "Ich bin eine Birne";
17   }
18 
19   class ObjectPrinter {
20     public ObjectPrinter(){
21       obstSammlung[0] = new Apfel();
22       obstSammlung[1] = new Birne();
23       // Der folgende Code führt zum Werfen einer Ausnahme
24       // obstSammlung[2] = new Birne();
25     }
26 
27     public void print(){
28       int laenge = obstSammlung.Length;
29       for(int i=0; i<laenge; i++){
30         Console.WriteLine(obstSammlung[i].ToString());
31       }
32     }
33 
34     private object[] obstSammlung = new object[2];
35   }
36 }

Ab Zeile 5 definiert Listing 2 die Klassen mit den Namen »Apfel« und »Birne«. Anders als in C++ darf der Programmierer private Variablen direkt im Klassenrumpf initialisieren. Der Konstruktor kann auf diese Informationen zurückgreifen. Die beiden Klassen demonstrieren auch, wie sich in C# öffentliche und private Elemente erzeugen lassen. Die Klasse »ObjectPrinter« enthält ein Array von zwei Objekten, dem der Konstruktor jeweils ein Objekt vom Typ »Apfel« und eines vom Typ »Birne« zuweist.

Adressiert

Klassen sind Referenztypen. Das bedeutet, dass das Array »obstSammlung[]« nicht die Klassen oder deren Objekte selbst, sondern lediglich ihre Speicheradresse enthält. Dies können sich C- oder C++-Programmierer wie Pointer vorstellen, auch wenn sie durch die Referenz nicht ohne Weiteres Zugriff auf den Speicher selbst erhalten.

Putzdienst

Die mit »new« angelegten Referenztypen verwaltet in C# ein Garbage-Collector. Der Entwickler muss sich nicht selber um das Aufräumen kümmern. Dieser Ansatz ist komfortabel, gerät jedoch gelegentlich wegen seines Performance-Overheads in die Kritik. Herauszufinden, wann ein Objekt nicht mehr gebraucht wird und der Garbage-Collector es löschen darf, bedeutet zusätzlichen Aufwand.

Neben den Referenztypen kennt C# die Wertetypen wie »int«, »float«, »double«, »decimal« und »bool«. Die Schlüsselwörter »int« oder »bool« sind dabei nicht nur Kurzschreibweisen für die Klassen »System.Int32« und »System.Boolean«. Listing 1 hat demonstriert, wie sich bestimmte, im Sprachumfang enthalten Methoden weitervererben.

Listing 3 ergänzt Listing 2 um die sehr kurze Methode »Main«. Diese bindet den Namensraum der »ObjectPrinter«-Klasse aus Listing 2 ein, instanziiert ein passendes Objekt und ruft die »print()«-Methode des Objekts auf. Die beiden Dateien lassen sich mit »gmcs main.cs objectCollection.cs« übersetzen.

Der Aufruf von »mono main.exe«, gibt folgende Ausgabe zurück:

Im Apfel-Konstruktor: Ich bin ein Apfel
Im Birnen-Konstruktor: Ich bin eine Birne
In HalloWelt2.Main():
LinuxMagazineObjectPrinter.Apfel
LinuxMagazineObjectPrinter.Birne

Die ersten beiden Zeilen stammen von den Konstruktoren der Klassen »Apfel« und »Birne«. Die beiden letzten Zeilen erzeugen »Apfel.ToString()« sowie »Birne.ToString()«, die Zeile 30 über das Array »obstSammlung[]« indirekt aufruft.

Die Methode »ToString()« stammt aus der »System.Object«-Klasse der C#-Standard-Bibliothek. Sie ist aber nicht dafür ausgelegt, spezifische Informationen zu den beiden Klassen auszugeben. Sie liefert daher einfach den voll qualifizierten Namen inklusive Namensraum zurück. Wie sich die Umwandlung eines Objekts in einen String mit Hilfe von polymorpher Vererbung aufschlussreicher gestalten lässt, erläutert der nächste Teil der C#-Serie.

Listing 3:
»main.cs«

01 using System;
02 using LinuxMagazineObjectPrinter;
03 
04 namespace LinuxMagazinHelloWorld2
05 {
06   class HalloWelt2
07   {
08     public static void Main(string[] args)
09     {
10       ObjectPrinter opr = new ObjectPrinter();
11       Console.WriteLine("In HalloWelt2.Main():");
12       opr.print();
13     }
14   }
15 }

Schweizer Taschenmesser

Stabil liegt die komfortable Entwicklungsumgebung Monodevelop bisher erst in Version 1.0 vor [9]. Momentan fehlen noch einige wünschenswerte Features, Version 2.0 steht aber schon vor der Tür. Monodevelop 1.0 ist in den verbreiteten Distributionen enthalten. Aktuellere Versionen sind eventuell auf dem Open-Suse-Buildservice [11] zu finden.

Die IDE bietet beim Anlegen von Projekten Standard-Projekttypen an, darunter den Typ »Gtk# 2.0«. Um den Dialog mit dem Text »Hallo Linux-Magazin« aus Abbildung 2 anzuzeigen, sind nur wenige Änderungen an dieser Vorlage erforderlich. Wie andere IDEs geht auch Monodevelop über das Syntax-Highlighting und die Schlüsselwort-Vervollständigung gewöhnlicher Editoren hinaus. Abbildung 3 zeigt den Code-Vervollständigungsdialog, der sämtliche Methoden der Klasse »win« aus der Standardbibliothek mitsamt einer kurzen Dokumentation präsentiert.

Abbildung 2: Auch wenn es sich noch nicht mit Microsofts Virtual Studio messen kann, bietet Monodevelop dennoch eine komfortable Entwicklungsumgebung für C# und andere Sprachen.

Abbildung 2: Auch wenn es sich noch nicht mit Microsofts Virtual Studio messen kann, bietet Monodevelop dennoch eine komfortable Entwicklungsumgebung für C# und andere Sprachen.

Abbildung 3: Die Eingabevervollständigung von Monodevelop umfasst nicht nur Schlüsselwörter, sondern auch die Methoden von Standardklassen.

Abbildung 3: Die Eingabevervollständigung von Monodevelop umfasst nicht nur Schlüsselwörter, sondern auch die Methoden von Standardklassen.

Bewegliches Ziel

C# ist eine ausdrucksstarke Sprache. Die Open-Source-Welt und Linux können von einer freien Implementierung nur profitieren. Wie bei allen Open-Source-Projekten, die eine proprietäre Programmierumgebung nachbilden, liegt die größte Herausforderung darin, mit der zügigen Weiterentwicklung in Redmond mitzuhalten. Mit der bevorstehenden Release von Version 2.0 des Mono-Frameworks ist dies, so zeichnet es sich schon ab, ein gutes Stück weit gelungen. (pkr)

Infos

[1] Dotnet bei Microsoft: [http://www.microsoft.com/net]

[2] Mono-Projekt: [http://www.mono-project.com]

[3] Mono 2.0 (Draft) Release Notes: [http://www.go-mono.com/archive/2.0/]

[4] Von Mono unterstützte Sprachen: [http://www.mono-project.com/Languages]

[5] Mono-Dokumentation: [http://www.go-mono.com/docs/]

[6] Mono-FAQs: [http://www.mono-project.com/FAQ:_General]

[7] Mono-Binary-Installer: [http://mono.ximian.com/monobuild/preview/download-preview/]

[8] C#-Mode für Emacs: [http://mfgames.com/linux/csharp-mode]

[9] Monodevelop: [http://www.monodevelop.com]

[10] Dokumentationskommentare: [http://msdn.microsoft.com/de-de/library/b2s063f7.aspx]

[11] Open-Suse-Buildservice: [https://build.opensuse.org]

Der Autor

Dr. Rüdiger Berlich arbeitet seit 1992 mit Linux und Open Source. Er bereitet am Karlsruhe Institute of Technology einen Open-Source-Spinoff aus dem Bereich der verteilten Optimierung vor.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben