C-Sharp-Workshop, Teil 1: Einstieg in die C-Sharp-Welt

Im ersten Teil des C#-Workshops werden die grundlegenden Konzepte und Eigenheiten der Programmiersprache anhand einfacher Beispiele vorgestellt.

Artikel erschienen in Swiss IT Magazine 2001/22

     

Als die Fachwelt vor etwa einem Jahr zum ersten Mal hörte, Microsoft wolle eine neue Programmiersprache einführen, schien dieses vielen ein wenig befremdlich. C Sharp sollte der Name sein, geschrieben als C#. Es stellte sich bald die Frage, ob es sich dabei um einen Nachfolger von C++ handeln würde oder allenfalls auch um ein Gegenstück zu Java, proprietär auf die Microsoft-Welt abgestimmt.



Ein paar Monate später lüftete sich dann ein weiteres Geheimnis: Microsoft stellte .Net vor. Zu diesem Zeitpunkt wurde klar, dass Microsoft nicht mehr die Frage der Programmiersprache in den Vordergrund stellt. .Net ist ein für Programmiersprachen offenes Konzept und C# ist eine mögliche Sprache zur Programmentwicklung unter .Net.




Das Interesse vieler Entwickler wurde an C# voll und ganz geweckt, als zu erfahren war, wer hinter .Net steckt. Es ist Anders Hejlsberg, der Vater von Turbo Pascal und seit 1996 Microsoft-Mitarbeiter.


Die Positionierung von C#

Das Ziel von Microsoft war die Entwicklung der ersten komponentenorientierten Sprache für die C/C++-Familie. Softwareentwicklung heutzutage bedeutet immer weniger die Erstellung monolithischer Anwendungen. Vielmehr werden Komponenten entworfen, die sich in unterschiedliche Ausführungsumgebungen integrieren. Also beispielsweise transaktionale Geschäftsobjekte, Steuerelemente für Browser oder GUI-Oberflächen und Funktionsbibliotheken. Dieses verlangt nach einer Sprache, die Objekte mit Eigenschaften, Methoden, Ereignissen und beschreibenden Attributen in einfacher Form sowohl erstellen als auch verwenden kann. C# ist eine Sprache, in der alles auf Objekten basiert.



C# ist die systemeigene Hochsprache von .Net. C# wurde konsequent auf die Erstellung von robusten und langlebigen Komponenten entwickelt. Im Gegensatz zu C++ ist die Sprache wesentlich stärker objektorientiert ausgelegt. C- oder C++-Entwickler können ihr vorhandenes Wissen jedoch weiterverwenden, da C# eine sehr starke Ähnlichkeit vor allem zu C++ enthält. Sie finden aber grosse Erleichterungen bei der Programmierung; Includes und Header-Dateien sind nun nicht mehr notwendig. Auch Java-Programmierer werden sich sehr schnell in C# zu Hause fühlen. Seine Leistungsfähigkeit bezieht C# vor allem durch die Nutzung der .Net-Laufzeitumgebung und des .Net Frameworks.





Die .Net-Laufzeitumgebung

Bevor die Programmiersprache nun im Detail vorgestellt wird, zuerst eine kurze Einführung in die grundsätzliche Funktionsweise der .Net-Laufzeitumgebung. Es würde den Rahmen dieses Workshops sprengen, alle Einzelheiten der .Net-Laufzeitumgebung zu besprechen.



Mit einem herkömmlichen Compiler unter Windows werden eigenständig ausführbare Programme als .exe-Dateien oder Programmbibliotheken als .dll-Dateien erstellt. In den letzten Jahren gewann auch das Erstellen von Komponenten, die innerhalb einer anderen Ausführungsumgebung ausgeführt werden, eine immer grössere Bedeutung. Der Standard zur Erstellung solcher Komponenten wurde von Microsoft über das COM-Modell festgelegt. Allen gemeinsam ist, dass immer das Betriebssystem Windows in entsprechenden Versionen notwendig ist, sollen die erzeugten Programme, Bibliotheken oder Komponenten ausgeführt werden. Diese liegen nämlich in Maschinencode vor und sind in der Regel zusätzlich auf entsprechenden Laufzeitumgebungen, z.B. die MFC oder VisualBasic Runtime, angewiesen.




Ein .Net Compiler wie C# erzeugt nun keine direkt ausführbaren Programme oder Komponenten, er erstellt vielmehr einen Code im Microsoft-eigenen Intermediate-Language-Format (IL). Es werden als Ziel der Kompilierung zwar auch weiterhin .exe oder .dll-Files angegeben, diese werden unter .Net jedoch als Assemblierung bezeichnet, die eben IL-Anweisungen statt Maschinencode enthalten. Prinzipiell kann eine Assemblierung sogar aus mehreren Dateien bestehen.



Diese Assemblierungen sind dann von der .Net-Laufzeitumgebung ausführbar. Dazu wird ein Just In Time Compiler (JIT) verwendet, der eine Assemblierung endgültig in Maschinencode übersetzt. Dies geschieht zu dem Zeitpunkt, an dem die Assemblierung installiert wird, oder dynamisch bei der ersten Verwendung einer Assemblierung. Die Vorgehensweise erlaubt die Anpassung des ausführbaren Codes an unterschiedliche Prozessoren. Daneben schafft es jedoch auch die Unabhängigkeit vom Betriebssystem, entscheidend ist nur die Verfügbarkeit der .Net-Laufzeitumgebung für das Zielsystem.



Da die Laufzeitumgebung immer optimierten Maschinencode ausführt, ergeben sich deutliche Geschwindigkeitsvorteile gegenüber anderen Konzepten virtueller Maschinen, die lediglich einen Zwischencode ausführen.




Erweiterung per Metadaten

Assemblierungen bestehen jedoch nicht nur aus IL-Code, sondern enthalten auch Metadaten, die ein Objekt so beschreiben, dass es innerhalb von .Net ohne zusätzliche Dateien oder Einträgen in die Registry eingesetzt werden kann. Zu diesen Objektinformationen zählen Objektname, alle Eigenschaften sowie die Namen der Mitgliedsfunktionen einschliesslich Parameterdefinition. Dieses Konzept vereinfacht sowohl die Entwicklung als auch die Benutzung von Assemblierungen. Im Gegensatz zur Entwicklung unter COM ist keine Registrierung mehr nötig, die Installation von Anwendungen per XCOPY-Befehl wird möglich. Über das sogenannte "Reflection" ermöglicht die .Net-Laufzeitumgebung das Auslesen dieser Metadaten zur Laufzeit.
Die Definition von Attributen erlaubt die weitere Auszeichnung von Komponenten. Diese werden ebenfalls in den Metadaten gespeichert und bieten beispielsweise die Möglichkeit zur Festlegung des Transaktionsverhaltens einer Komponente.



Mit C# entwickelte Programme und Komponenten können aufgrund der Integration in die .Net-Laufzeitumgebung nun ohne jegliche Probleme von allen anderen Programmiersprachen unter .Net verwendet werden. Entscheidend hiefür ist vor allem die einheitliche Typendefinition unter .Net, die allen Programmiersprachen zur Verfügung steht.




Da für .Net geschriebene Programme grundsätzlich innerhalb der .Net-eigenen Speicherverwaltung ablaufen, ist ein manuelles Speichermanagement nicht mehr notwendig und auch nicht mehr möglich. Freigegebener und unbenutzter Speicher wird durch den sogenannten Garbage Collector verwaltet.




Das erste Programm in C#

Traditionell stellt sich eine Programmiersprache immer über die Ausgabe des Textes "Hello World" vor. Das folgende Beispiel zeigt den in C# notwendigen Code. Das Programm ist als Konsolenanwendung realisiert.



Bevor die einzelnen Programmzeilen im Detail untersucht werden, hier noch drei grundsätzliche Syntaxdefinitionen: Codeblöcke werden generell in geschweifte Klammern eingeschlossen, Anweisungen werden mit einem Semikolon abgeschlossen und Kommentare durch zwei Slashes (//) eingeleitet.




In der ersten Zeile wird ein Namespace definiert. Namespaces sind die Grundlage zur Benennung von Komponenten in .Net. Jede Komponente sollte einen eigenen Namespace besitzen. Namespaces können hierarchisch gegliedert und somit auch verschachtelt definiert werden.



In der nächstfolgenden Zeile wird durch das Schlüsselwort Using der Namespace System aus dem .Net Framework verwendet. Namespaces werden in Assemblierungen festgelegt. Der Name der Assemblierung, der den entsprechenden Namespace enthält, muss dem Compiler als Referenz bekannt sein. So befindet sich beispielsweise der Namespace System in der Assemblierungsdatei system.dll.



Die Klasse mit Namen Hello wird als mit public veröffentlicht, um anderen Programmen die Möglichkeit zu geben, diese zu benutzen.



C# kennt keine globalen Funktionen. Daher wird innerhalb der Klasse Hello eine statische Funktion definiert, die als Startpunkt der Anwendung dient. Über die Definition von HelloWorld.Hello als Startobjekt wird diese Klasse beim Starten der Anwendung automatisch instanziert, worauf die Funktion Main ausgeführt wird.



Diese Funktion gibt über Console.Writeline einen Text im Konsolenfenster aus. Die Klasse Console ist innerhalb des Namespace System definiert. Da über using die Metadaten des Namespace System bereits bekannt sind, kann hier die Kurzschreibweise verwendet werden. Die folgende Tabelle zeigt, wie sich der Einsatz von using auf den Programmieraufwand auswirkt.




Kompilieren des Programms

Es ist nicht notwendig, Visual Studio.Net als Tool zur Erstellung von Programmen zu verwenden. Da der Compiler auch als Kommandozeilenanwendung csc.exe zur Verfügung steht, reicht ein einfacher Texteditor aus. Über entsprechende Parameter können weitere Optionen gesetzt werden, beispielsweise die verwendeten Verweise oder eine Antwortdatei. Interessant ist an dieser Stelle noch, dass beispielsweise mehrere Klassen innerhalb einer einzigen Quellcodedatei definiert werden können.





Datentypen in C#

Bei der Verwendung von Datentypen greift C# auf die unter .Net grundsätzlich verfügbaren Datentypen zurück. So ist ein int in C# mit dem Typ System.Int32 aus der .Net-Laufzeitumgebung vorhanden.



Datentypen werden in Werte- und Verweistypen unterteilt. Wertetypen werden dem Stack zugewiesen oder sind strukturintern vorhanden. Verweistypen werden dem Heap zugeordnet. Alle Datentypen sind von der Basisklasse object abgeleitet. Muss nun ein Wertetyp als Verweistyp fungieren, so wird ein Wrapper, der den Wertetyp als Verweisobjekt erscheinen lässt, dem Heap zugeordnet. Dieses wird als Boxing bezeichnet, der umgekehrte Vorgang heisst Unboxing. Erreicht wird hiermit, dass jeder beliebige Datentyp als Objekt behandelt werden kann. Der folgende Code zeigt dies anhand einer Ganzzahl, die als Literal 4711 im Sourcecode definiert wird. Obwohl dieses natürlich ein ganzzahliger Wertetyp ist, kann die Methode .ToString() durch Boxing angewandt werden.






Durch das Schlüsselwort class wird ein Verweistyp definiert. Im obigen Beispiel wird dieses also mit dem Namen BoxingDemo gemacht. Strukturen werden durch das Schlüsselwort struct eingeleitet. Im Gegensatz zu class werden hier Wertetypen deklariert. Strukturen sollten nur für schlanke Objekte eingesetzt werden, die wie integrierte Typen agieren. Ansonsten sind Klassen vorzuziehen.




Die Programmsteuerung

Bei der Benutzung von Operatoren haben alle diejenigen, die der Sprache C++ mächtig sind, keine Probleme, da die Ausdruckssyntax von C# mit der von C++ identisch ist. Für den Neueinsteiger in die C-Sprachenfamilie zeigt die folgende Tabelle einen Überblick über alle arithmetischen Operatoren.



Der Operator + kann auch zur Zeichenfolgeverkettung eingesetzt werden. Operanten, die in einer solcher Anweisung nicht vom Typ string sind, können über den Aufruf der virtuellen Methode ToString() automatisch in eine Zeichenfolge umgewandelt werden.





Über ein vorgestelltes Minus kann ein Wert negiert werden, sofern er einen Datentyp besitzt, der über eine gültige negative Darstellung verfügt.



Ein Vergleich zweier Werte erfolgt mit relationalen Operatoren. Um einen boolschen Wert zu negieren, wird weiter der Operator ! eingesetzt.



Die relationalen und logischen Operatoren von C# haben wir im Kasten auf der nächsten Seite zusammengestellt. Die Sprache C# stellt auch einen Bedingungsoperator als ?: zur Verfügung. Dieser wählt basierend auf einem boolschen Ausdruck aus zwei Ausdrücken aus.





Artikel kommentieren
Kommentare werden vor der Freischaltung durch die Redaktion geprüft.

Anti-Spam-Frage: Aus welcher Stadt stammten die Bremer Stadtmusikanten?
GOLD SPONSOREN
SPONSOREN & PARTNER