Das Mono-Projekt ist die freie Antwort auf Microsofts .NET-Plattform. Dieser Artikel stellt grundlegende Klassen und Methoden der Framework Class Library vor und demonstriert, wie man mit C# bequem Dateien, Zeichenketten und Collections verarbeitet.
Zu Mono[1] ist in letzter Zeit schon viel gesagt und geschrieben worden, deshalb gibt es an dieser Stelle nur ein paar Eckdaten zum Aufwärmen. Das Projekt wurde von Miguel de Icaza und anderen Entwicklern vor einigen Jahren ins Leben gerufen. Mono ist die Open-Source-Implementierung des .NET-Framework für verschiedene Plattformen. Wie das Original basiert es auf der Common Language Infrastructure (CLI). Ähnlich wie Java besteht das Mono-Environment aus einem Compiler, einer Virtual Machine und Myriaden von API-Klassen. Noch liegt der Schwerpunkt des Compilers auf C# – das wird sich in absehbarer Zeit aber ändern.
Freie Spezifikation
Die Entscheidung, die ursprünglich proprietäre Sprache C# (von Microsoft) auch für Linux verfügbar zu machen, geht unter anderem auf Erfahrungen bei der Gnome-Entwicklung zurück und hat seinerzeit für erhebliche Kontroversen gesorgt. Das Ziel ist, in Zukunft unter Linux eine moderne, einfach benutzbare und zugleich leistungsfähige Sprache wie C++ zu haben, aber ohne deren Nachteile. Die Installation von Mono ist nicht schwer, für die meisten Distributionen gibt es fertige Binärpakete, siehe Kasten “Installation”.
Die Framework Class Library (FCL), das mächtige API des Mono-Framework, ist eine Klassenbibliothek, wie es sie auch in Programmiersprachen wie Java, Python und C++ gibt. Bibliotheken dieser Größenordnung sind nicht auf Anhieb zu verstehen oder gar zu überschauen. Vielmehr muss man eine Menge Zeit investieren, da sie oft aus mehreren tausend Klassen bestehen. Am Anfang braucht der Anwender aber nicht jedes Detail der FCL zu kennen.
Mono bringt mit »monop« sogar ein eigenes Tool für die Navigation durch die Klassenbibliothek mit. Mit der Option »-c« und einem Klassennamen aufgerufen zeigt es die entsprechenden Unterklassen an, bei einer Methode deren Signatur, siehe Abbildung 1.
Der Beitrag stellt wichtige Basisklassen vor, die tatsächlich bei der täglichen Arbeit unentbehrlich sind, und zeigt anhand von Beispielen typische Anwendungsbereiche. So ist es recht einfach, mit der Klassenbibliothek Dateien zu lesen, zu schreiben oder anderweitig zu bearbeiten. Die Klassen zur Verarbeitung von Zeichenketten sind in diesem Zusammenhang ebenfalls wichtig. So genannte Collections (Datentypen für Sammlungen wie »ArrayList«, »Hashtables«) sind in jeder Programmiersprache von zentraler Bedeutung, um eine angemessene Datenrepräsentation zu gewährleisten.
Dateien verarbeiten
C# stellt für die Bearbeitung von Dateien eine Menge komfortabler Konstrukte bereit und macht die Arbeit damit fast zum Kinderspiel. In der Framework Class Library finden sich für diesen Zweck die passenden Klassen: »File«, »TextReader« und »TextWriter«. Wer damit anfängt, selbst C#-Sourcecode zu schreiben, findet einige Hinweise dazu im Kasten “C # editieren”.
|
Installation |
|---|
|
Überhaupt kein Problem ist die Installation für Anwender, die Suse-Linux oder Fedora einsetzen, denn dafür gibt es unter[2] schon passende Pakete. Wer als Fedora-Nutzer das entsprechende Repository einträgt, aktualisiert seine Mono-Pakete in Zukunft bequem über Yum, siehe[3]. Debian-Benutzer werden unter[4] fündig. Für andere Plattformen ist aber möglicherweise der Griff zum Compiler erforderlich. |

Abbildung 1: Mit dem Befehl »monop -c« lässt sich der Namensraum der Klassenbibliothek erkunden. Hier listet das Kommandozeilenprogramm die Interfaces und Klassen des Namespace »System« auf.
Listing 1 zeigt das erste C#-Beispiel, das die Handhabung der erwähnten Klassen demonstriert. Im Gegensatz zu Java ist es C# übrigens egal, ob Klassen- und Dateiname identisch sind. Das Programm in Listing 1 lässt sich folgendermaßen übersetzen:
mcs -o FH.exe FileHandler.cs
Der Compiler »mcs« ist das Gegenstück zum »csc« unter Windows und erzeugt aus dem Quelltext die ausführbare Datei; wer will, kann auch einen anderen Namen wählen oder den »-o«-Schalter einfach weglassen.
Der Befehl »file«, auf das Endprodukt angewandt, sorgt für eine Überraschung:
file FH.exe FH.exe: MS Windows PE 32-bit Intel 80386 console executable
|
Listing 1: Umgang mit Dateien: |
|---|
01 using System;
02 using Con = System.Console;
03 using System.IO;
04
05 public class FileHandler {
06
07 public static void Main(string [] args) {
08 string file = "";
09
10 try {
11 file = args[0];
12 } catch (System.IndexOutOfRangeException) {
13 Con.WriteLine("Bitte Dateinamen angeben.");
14 Environment.Exit(1);
15 }
16
17 if (File.Exists(file)) {
18 Con.WriteLine("Datei existiert: " + file);
19 } else {
20 using (TextWriter writer = File.CreateText(file)) {
21 writer.WriteLine("Hallo von Mono");
22 }
23 }
24 using (TextReader reader = File.OpenText(file)) {
25 Con.WriteLine("n" + reader.ReadToEnd());
26 }
27 }
28 }
|
Es ist kaum zu glauben, aber es gibt nun tatsächlich Exe-Dateien auch unter Linux. Dabei handelt es sich, wie bei den ebenfalls auftretenden DLLs, um so genannte Assemblies, die den ausführbaren Bytecode und Metainformationen enthalten.
Die kompilierte Datei führt man mit dem Aufruf »mono FH.exe« aus, der den Just-in-time-Compiler startet und dessen Endprodukt ausführt. Alternativ arbeitet der Interpreter »mint« die Exe-Datei ab. Mit dem Binfmt-Misc-Modul des Kernels lassen sich Mono-Bytecode-Dateien auch direkt ausführen, siehe Kasten “Direkt ausführen”.
Im Code finden sich zwei Arten von »using«-Statements in unterschiedlichen Kontexten: am Anfang, um die Namensräume der Bibliothek verfügbar zu machen (Zeilen 1 bis 3), und dann noch zweimal in ganz anderer Weise weiter hinten im Listing (Zeilen 15 und 22).
Automatische Freigabe von Ressourcen
Wer sich Listing 1 genau angeschaut hat, vermisst vielleicht eine C#-Anweisung, die das File wieder schließt. Genau hier kommt das andere »using« ins Spiel, das eine Ressource allokiert, in einem Block verwendet und danach wieder freigibt. Die Klassen »TextReader« und »TextWriter« implementieren dafür »IDisposable«, was dem Programmierer das Schließen der Datei und die anschließende Freigabe der Systemressourcen erspart.
Selbstverständlich bringt Mono auch Klassen für binäre Dateien mit, nämlich »BinaryReader« und »BinaryWriter«. Beispielsweise verfügt »BinaryReader« über Methoden wie »ReadByte()« und »ReadDouble()«, die binäre Daten aus Streams lesen. In der mitgelieferten Dokumentation finden sich dazu mehr Details.
Praktische Strings
Strings sind in C#, wie in Java und vielen anderen modernen Sprachen auch, unveränderbar (immutable). Beim Arbeiten mit Strings entsteht also immer eine Kopie. Listing 2 zeigt verschiedene Aufrufe von String-Konstruktoren. Bemerkenswert ist die Initialisierung von »b« (Zeilen 10 und 11). Man könnte vom Compiler die Meldung eines Syntaxfehlers erwarten, denn bei anderen Programmiersprachen müsste der Zeilenumbruch im String mit einem Backslash »« ausmaskiert werden. Doch er erzeugt die Exe-Datei anstandslos. Dafür sorgt der spezielle Formatierer »@«, der für so genannte Verbatim (wortgetreue) String Literals steht.
|
C # editieren |
|---|
|
Für alle, die Artikel-Beispiele nachvollziehen möchten: Man kann unter Linux mit neuen Vim-Versionen[5] C #<$>-Dateien anlegen und editieren. Dieser leistungsfähige Editor erkennt die C#<$>-Syntax auf Anhieb und stellt sie farbig dar, siehe Abbildung 2. Aber auch für XEmacs/Emacs gibt es neuerdings einen optionalen C #<$>-Mode von Brad Merrill unter[6]. Selbst das Java-Entwicklungstool Eclipse besitzt ein C#<$>-Plugin[7] – dessen Sinnhaftigkeit sei mal dahingestellt. Sehr gut unterstützt Monodevelop die Sprachsyntax, aber auch die Klassenbibliotheken[8]. So bietet es kontextsensitive Vervollständigung von Klassen-, Methoden- und Variablennamen. |

Abbildung 2: Der Editor Vim unterstützt in neuen Versionen C#-Code durch farbiges Syntax-Highlighting.
Bei der Ausgabe mit »Con.WritLine()« verkettet ein »+« zwei Strings (Zeile 8). Die String-Methode »IndexOf()« sucht das erste Auftreten eines Zeichens (Zeile 14), das auch im Unicode-Format angegeben sein darf. »Replace()« ersetzt jedes Vorkommen eines Zeichens durch ein anderes (Zeile 17) und »ToLower()« wandelt den String in Kleinbuchstaben um (Zeile 20).
|
Listing 2: Arbeit mit |
|---|
01 using System;
02 using Con = System.Console;
03
04 public class Strings {
05
06 public static void Main(string [] args) {
07 string a = "Zeichenkette";
08 Con.WriteLine("String a:" + a);
09
10 string b = @"Ein String mit
11 Zeilenumbruch";
12 Con.WriteLine("String b:" + b);
13
14 int i = b.IndexOf('u000a'); //
ASCII/Unicode für n
15 Con.WriteLine("Zeilenumbruch bei Zeichen "
+ i.ToString());
16
17 string c = b.Replace('u000a', '_');
18 Con.WriteLine("String c:" + c + "?");
19
20 string d = c.ToLower();
21 Con.WriteLine("String d:" + d + "?");
22 }
23 }
24
|
|
Listing 3: Collections: |
|---|
01 using System;
02 using Con = System.Console;
03 using System.Collections;
04
05 public class Env {
06
07 public static void Main(string [] args) {
08 IDictionary env = Environment.GetEnvironmentVariables();
09
10 string key = "USER";
11 if (env.Contains(key)) {
12 Con.WriteLine("{0}={1}", key, env[key]);
13 }
14
15 foreach (string k in env.Keys) {
16 Con.WriteLine("{0}={1}", k, env[k]);
17 }
18 }
19 }
|
Wie erwähnt sind Strings unveränderlich. Die Verkettungen (Concatenations) mit dem Plus-Operator sind deshalb in der Regel ziemlich aufwändig, denn es müssen immer wieder Kopien erzeugt werden. Aber glücklicherweise bietet Mono mit seiner Klasse »StringBuilder()« aus dem Namensraum »System.Text« eine viel effizientere Alternative für die String-Erzeugung zur Laufzeit:
StringBuilder d = new StringBuilder(); d.Append(c); d.Append(a); Console.WriteLine(d.ToString());
Man kann sogar noch einen Schritt weiter gehen und die beiden Aufrufe in einer Zeile miteinander verbinden:
StringBuilder d = new StringBuilder(c).Append(a);
Diese Art der String-Verarbeitung ist kompakt und effektiv zugleich. Java-Entwickler werden unweigerlich an »StringBuffer()« denken.
Sammlungen
Auch bei komplexeren Datentypen wie Collections bietet C# viel Komfort für geplagte Entwickler. Die Klasse in Listing 3 sucht erst nach einer Umgebungsvariablen mit dem Key »USER« (Zeilen 10 bis 13) und iteriert dann über die ganze Collection (Zeilen 15 bis 17). Die Werte gibt das Programm auf der Konsole aus, siehe Abbildung 3.
Java-Entwicklern wird dieser Code wahrscheinlich nur ein müdes Lächeln entlocken, andere werden aber wegen der Einfachheit der Verwendung von Collections eventuell neugierig.
Im Namensraum »System.Diagnostics« sind einige Klassen zu finden, die Informationen zu Prozessen liefern, siehe Listing 4. Leider verhalten sich nicht alle Methoden so, wie in der .NET-Spezifikation vorgesehen: Die Methode »Get AllProcesses()« gibt unter Linux nur die Prozesse innerhalb des Mono-Kontexts aus, also meist nur das Programm selbst.
Auch andere systemnahe Methoden können ihre Herkunft aus der Windows-Welt nicht verbergen. So repräsentiert die Aufzählung »System.IO.FileAttributes« Dateimerkmale wie Archiv oder System, die nur unter Windows existieren oder sinnvoll sind.
Fazit
In der Welt der Entwickler geht es zurzeit spannend zu. Bisher einander fremde Systeme und Technologien scheinen zunehmend zu verschmelzen: Eine Microsoft-Sprache steht unter Linux zur Verfügung, Mono wiederum läuft unter Windows. So planen die Autoren von “Mono: A Developers Notebook”[9], die C#-Beispiele ihres Buches demnächst zusätzlich in Iron Python[10] zu schreiben, einer Python-Implementierung für die .NET-Plattform. Der Iron-Python-Autor Jim Hugunin arbeitet seit kurzer Zeit sogar für Microsoft.
|
Direkt |
|---|
|
Wer das passende Kernel-Feature einkompiliert oder als Modul übersetzt, kann Mono- Bytecode auch direkt ausführen. Startet eine solche Exe-Datei von der Kommandozeile, dann sorgt der Kernel für die Ausführung des Interpreters »mono«. Bei einem Kernel-Compile besteht die Möglichkeit, die entsprechende Option unter »Executable file formats« mit »Kernel support for MISC binaries« einzustellen. Ist das Übersetzen (»make modules«) und Installieren (»make modules_install«) erledigt, lädt der Anwender das Modul mit »/sbin/modprobe binfmt_misc«. Sollte der Automounter das dafür erforderliche Pseudo-Dateisystem nicht selbstständig einhängen, hilft der folgende Befehl weiter: mount -t binfmt_misc none /proc/sys/fs/binfmt_misc Darüber hinaus muss der Kernel noch die Signatur der Bytecode-Dateien erfahren: echo ':CLR:M::MZ::/usr/bin/mono:' > /proc/sys/fs/binfmt_misc/register Wenn die Exe-Dateien Ausführungsrechte (»chmod +x«) besitzen, lassen sie sich anschließend direkt ausführen. |
Es ist beeindruckend, was innerhalb weniger Jahre aus Mono geworden ist. Miguel de Icaza und seine Mitstreiter haben sich nicht beirren lassen und an ihrer Vision festgehalten. Der Einsatz hat sich gelohnt, zumal nun Novell stark auf Mono setzt. Auch Redmond hat die Zeichen der Zeit erkannt und unterstützt das engagierte Projekt partiell. Schließlich verbreitet sich auf diese Weise die hauseigene Sprache C# auch eher auf fremden Plattformen.
Das konkurrierende Java-Lager ist dagegen uneins: IBM, Sun und die Open-Source-Gemeinde ringen derzeit noch um einen Konsens. Vielleicht wird schließlich aus Mono/C# so etwas, was Sun Microsystems einst mit Java erreichen wollte. Das Potenzial dazu hat die Sprache allemal, sie ist so mächtig wie C++ und überdies ECMA-standardisiert. Man darf also gespannt sein, wie es mit Mono weitergeht.
|
Listing 4: Prozess-Handling: |
|---|
01 using System;
02 using Con = System.Console;
03 using System.Diagnostics;
04
05 public class ProcList {
06
07 public static void Main(string [] args) {
08
09 Process proc = Process.GetCurrentProcess();
10 Con.WriteLine("Process {0}: {1} auf {2} gestartet um {3}",
11 proc.Id, proc.ProcessName, proc.MachineName,
12 proc.StartTime);
13 Con.WriteLine("Priorität:" + proc.PriorityClass);
14 Con.WriteLine("Main Module: " + proc.MainModule);
15 }
16 }
|
|
Infos |
|---|
|
[1] Mono-Homepage: [http://www.mono-project.com/about/] [2] Mono-Downloads: [http://www.mono-project.com/downloads/] [3] Roman Jordan, “Fedora mit Yum updaten”: LinuxUser 10/04, S. 52, [http://www.linux-user.de/ausgabe/2004/10/052-yum/] [4] Mono für Debian: [http://pkg-mono.alioth.debian.org] [5] Vim: [http://www.vim.org] [6] Emacs-Mode für C #: [http://www.cybercom.net/~zbrad/DotNet/Emacs/] [7] C #-Plugin für Eclipse: [http://www.improve-technologies.com/alpha/esharp/] [8] Monodevelop: [http://www.monodevelop.com] [9] E. Dumbill, N. Bornstein, “Mono – A Developers Notebook”: O\’Reilly 2004 [10] Iron Python: [http://ironpython.com] |
|
Der Autor |
|---|
|
Thomas Kaufmann ist als freier Autor (Python, C, C #, Java) und Entwickler (Python, C, C #, Java) tätig. Zudem gehören die Administration von Linux-Systemen (Suse, Red Hat) sowie IT-Sicherheit zu seinen bevorzugten Themen. |






