## 1.2 .NET unter die Lupe genommen In den folgenden Abschnitten schauen wir uns jetzt ein wenig genauer an, was es mit .NET auf sich hat. ### 1.2.1 Das Entwicklerdilemma Mit .NET hat Microsoft im Jahr 2002 eine Entwicklungsplattform veröffentlicht, die inzwischen von vielen Entwicklungsteams akzeptiert und auch eingesetzt wird. Kommerzielle Gründe spielten für Microsoft sicherlich auch eine Rolle, damals einen Neuanfang in der Philosophie seiner Softwareentwicklung herbeizuführen. In den Jahren zuvor hatte sich bereits abgezeichnet, dass sich die Ansprüche an moderne Software grundlegend ändern würden. Das Internet spielte dabei wohl die wesentlichste Rolle, aber auch die Anforderung, dem erhöhten Aufkommen clientseitiger Anfragen an einen Zentralserver durch skalierbare Anwendungen zu begegnen. Der Erfolg von Java, das sich in den Jahren zuvor als eine der bedeutendsten Programmiersprachen etablierte, mag der Beweis dafür sein, denn Java spielt seine Stärken in erster Linie bei der Entwicklung webbasierter und verteilter Anwendungen aus. Die damaligen Probleme waren nicht neu, und entsprechende Technologien gab es schon länger – auch bei Microsoft. Mit COM/COM+ ließen sich zwar auch vielschichtige und skalierbare Anwendungen entwickeln, aber unzweifelhaft war die Programmierung von COM+ wegen der damit verbundenen Komplexität als nicht einfach zu bezeichnen. Es gibt nicht sehr viele Entwickler, die von sich behaupten können, diese Technologie im Griff gehabt zu haben. Damit trat auch ein Folgeproblem auf, denn grundsätzlich gilt: Je komplizierter eine Technologie ist, desto fehleranfälliger wird die Software. Man muss nicht unbedingt ein Microsoft-Gegner sein, um zu sagen, dass selbst der Urheber dieser Technologien diese oft nur unzureichend in den hauseigenen Produkten umsetzt. Die Aussage, dass die Vorteile der .NET-Systemplattform nur der Entwicklung verteilter Systeme wie dem Internet zugutekommen, beschreibt ihre Möglichkeiten nur völlig unzureichend. Selbstverständlich lassen sich auch einfache Windows- und Konsolenanwendungen auf Basis von .NET entwickeln. Die Vorteile beziehen sich aber nicht nur auf Anwendungen selbst, sondern lösten auch ein Dilemma der Entwickler. Die Entscheidung für eine bestimmte Programmiersprache war in der Vergangenheit fast schon eine Glaubensfrage – nicht nur, was die Programmiersprache anging, denn die Festlegung auf eine bestimmte Sprache war auch die Entscheidung für eine bestimmte Funktions- bzw. Klassenbibliothek. Windows-Programme basieren alle auf der Systemschnittstelle einer Funktionssammlung, die als WinAPI-32 bezeichnet wird. Da diese Funktionssammlung einige Tausend Funktionen enthält, wurden verwandte Funktionalitäten in Klassen zusammengeführt und konnten über Methodenaufrufe angesprochen werden. Dieses Prinzip vereinfachte die Programmierung deutlich, aber bedauerlicherweise gab es nicht eine einzige, sondern gleich mehrere, herstellerspezifische Klassenbibliotheken, die zwar ein ähnliches Leistungsspektrum aufwiesen, aber grundlegend anders definiert waren. Die Microsoft Foundation Classes (MFC) für Visual C++ ist die Klassenbibliothek von Microsoft, und Borland-Inprise kochte mit der Object Windows Library (OWL) ein eigenes Süppchen. Der Wechsel von einer Programmiersprache zu einer anderen bedeutete in der Regel auch, sich in eine andere Bibliothek einzuarbeiten. Beides kostet nicht nur sehr viel Zeit, sondern bedeutet auch finanziellen Aufwand. Es mag fast erstaunen (oder auch nicht), dass es neben Windows tatsächlich auch noch andere Betriebssysteme gibt, denen man durchaus auch eine Existenzberechtigung zuschreiben muss. Die Entwickler von Java haben das schon vor Jahren erkannt und mit der Virtual Machine (VM) eine Komponente bereitgestellt, die auf verschiedene Betriebssystemplattformen portiert werden kann. Dies ist einer der größten Vorteile von Java und hat sicherlich viele Entscheidungsträger in den Unternehmen beeinflusst. Code lässt sich auf Windows-Plattformen entwickeln und auf einer Unix-Maschine installieren – ein reizvoller Gedanke, Investitionen von einem bestimmten System zu lösen und sie nicht daran zu binden. ### 1.2.2 .NET – ein paar allgemeine Eigenschaften Es ist kein Zufall, dass ich im vorigen Abschnitt öfter Java erwähnt habe. Wenn Sie das Konzept von Java kennen oder vielleicht in der Vergangenheit sogar mit Java programmiert haben, werden Sie sehr viele Parallelen zu .NET wiedererkennen. Microsoft ist in der Vergangenheit sicher nicht entgangen, worauf der Erfolg von Java zurückzuführen ist. In Kenntnis der Fakten hat man die Idee, die hinter Java steckt, übernommen und dabei versucht, die bekannten Schwachstellen des Ansatzes bzw. der Sprache auszumerzen. Es darf sich bei Ihnen jetzt allerdings nicht die Meinung festigen, .NET sei nur eine Kopie von Java – .NET hat die Messlatte spürbar höher gelegt. Wir wollen uns nun ansehen, welche wesentlichen programmiertechnischen Neuerungen .NET mit sich bringt: Objektorientierung .NET ist zu 100 % objektbasiert und bildet eine konsistente Schicht zur Anwendungsentwicklung. Es gibt keine Elemente, die sich nicht auf Objekte zurückführen lassen. Sogar so einfache Datentypen wie der Integer werden als Objekte behandelt. Auch Zugriffe auf das darunterliegende Betriebssystem werden durch Klassen gekapselt. WinAPI-32-Ersatz Langfristig beabsichtigt Microsoft, die Win32-API durch die Klassen des .NET Frameworks zu ersetzen. Damit verwischen auch die charakteristischen Merkmale der verschiedenen Sprachen. Ob eine Anwendung mit Visual Basic .NET programmiert wird oder mit C# oder C++ – es spielt keine Rolle mehr. Alle Sprachen greifen auf die gleiche Bibliothek zurück, sprachspezifische, operative Bibliotheken gibt es nicht mehr. Die Konsequenz ist, dass die Wahl einer bestimmten Sprache nicht mehr mit der Entscheidung gleichzusetzen ist, wie effizient eine Anwendung geschrieben werden kann oder was sie zu leisten imstande ist. Plattformunabhängigkeit Anwendungen, die auf .NET basieren, laufen in einer Umgebung, die mit der virtuellen Maschine von Java verglichen werden kann, in der erst zur Laufzeit einer Anwendung der Maschinencode erzeugt wird. Die Spezifikation der Laufzeitumgebung (Common Language Runtime – CLR) ist keine geheime Verschlusssache von Microsoft, sondern offen festgelegt. In letzter Konsequenz bedeutet das aber auch, dass sich die Common Language Runtime auch auf Plattformen portieren lässt, die nicht Windows heißen, z. B. auf Unix oder Linux. Als Beweis sei hier das Mono-Projekt genannt, mit dem .NET erfolgreich auf die Linux-Plattform portiert worden ist. Sprachunabhängigkeit Es spielt keine Rolle, in welcher Programmiersprache eine Komponente entwickelt wird. Eine in C# 2010 geschriebene Klasse kann aus VB.NET, F# oder jeder anderen .NET-konformen Sprache heraus aufgerufen werden, ohne den Umweg über eine spezifizierte Schnittstellentechnologie wie COM/COM+ gehen zu müssen. Darüber hinaus lässt sich beispielsweise eine in Visual C# implementierte Klasse auch aus einer VB.NET-Klasse ableiten – oder umgekehrt. Speicherverwaltung Die Freigabe von nicht mehr benötigtem Speicher war schon immer ein Problem. Unter .NET braucht sich ein Entwickler darum nicht mehr zu kümmern, da der im Hintergrund arbeitende Prozess des Garbage Collectors diese Aufgaben übernimmt und nicht mehr benötigte Objekte erkennt und automatisch aus dem Speicher entfernt. Weitergabe Ein .NET-Programm weiterzugeben ist viel einfacher geworden – insbesondere im Vergleich zu einem auf COM basierenden Programm, das Einträge in die Registrierungsdatenbank vornehmen muss. Im einfachsten Fall reicht es vollkommen aus, ein .NET-Programm (d. h. eine EXE- oder DLL-Datei) in das dafür vorgesehene Verzeichnis zu kopieren. Darüber hinaus ist aber auch die Verteilung mit einem Installationsassistenten und – ganz neu unter .NET 2.0 – mit ClickOnce möglich. ### 1.2.3 Das Sprachenkonzept Die drei Entwicklungssprachen, die in der Vergangenheit hauptsächlich das Bild in der Anwendungsentwicklung prägten, waren C++, Java und Visual Basic 6.0. Seit dem Jahr 2002 und dem Erscheinen des .NET Frameworks 1.0 gesellten sich noch die .NET-Sprachen dazu, allen voran C#. Betrachten wir jetzt nur die drei zuerst genannten Sprachen. Nehmen wir an, wir würden mit jeder ein einfaches ausführbares Programm schreiben. Wie sehen die Kompilate dieser drei Sprachen aus, und wie werden die drei Kompilate ausgeführt, wenn wir sie auf einen Rechner kopieren, auf dem nur das Betriebssystem installiert ist? Nach der Kompilierung des C/C++-Quellcodes erhalten wir eine .exe-Datei, die beispielsweise durch einen einfachen Doppelklick im Explorer des frisch installierten Rechners gestartet werden kann. Das Kompilat wird jedoch auf einer anderen Plattform nicht lauffähig sein, denn dazu wäre zuerst eine Neukompilierung erforderlich. Eine mit dem VB6-Compiler erzeugte ausführbare Datei kann auf unserer jungfräulichen Betriebssysteminstallation nicht sofort gestartet werden, obwohl die Dateiendung .exe lautet. Wir benötigen zur Ausführung einen Interpreter, d. h. das Laufzeitmodul von Visual Basic, der uns den kompilierten Zwischencode in den ausführbaren nativen CPU-Maschinencode übersetzt. Die Portierung eines VB-Programms auf eine andere Plattform ist nicht möglich. Java arbeitet prinzipiell ähnlich wie Visual Basic 6.0. Es wird ein Zwischencode generiert, der sogenannte Bytecode. Die kompilierten Dateien haben die Dateiendung .class. Zur Laufzeit wird dieser Code zuerst durch einen Interpreter geschickt, der als virtuelle Maschine (VM) bezeichnet wird. Vorausgesetzt, die VM wurde bei der Installation des Betriebssystems installiert, kann man die Java-Anwendung starten. Das Kompilat ist sogar plattformunabhängig und kann auch auf andere Systeme verteilt werden. Insbesondere die Plattformunabhängigkeit des Kompilats ist bisher ein deutliches Argument für viele Unternehmen gewesen, nicht nur in heterogenen Umgebungen verstärkt auf Java zu setzen. Entwickeln wir eine .NET-basierte Anwendung, ähnelt der Ablauf der Kompilierung bis zum Start der Laufzeitumgebung dem Ablauf unter Java. Zuerst wird ein Zwischencode erzeugt, der CPU-unabhängig ist. Die Dateiendung lautet .exe, wenn wir eine eigenstartfähige Anwendung entwickelt haben. Allerdings ist diese Datei nicht ohne Weiteres lauffähig: Sie benötigt zur Laufzeit einen »Endcompiler«, der den Zwischencode in nativen, plattformspezifischen Code übersetzt. Der Zwischencode einer .NET-Anwendung wird als MSIL-Code (Microsoft Intermediate Language) oder nur kurz als IL bezeichnet, und der Endcompiler wird JIT-Compiler (Just-In-Time) oder kurz JITter genannt. Hier klicken, um das Bild zu vergrößern Abbildung 1.1 Der Ablauf der Entwicklung eines .NET-Programms bis zur Laufzeit ### 1.2.4 Die Common Language Specification (CLS) Wenn Sie sich in Abbildung 1.1 den Prozessablauf vom Quellcode bis zur Ausführung einer .NET-Anwendung ansehen, müssten Sie sich sofort die Frage stellen, wo der Unterschied im Vergleich zu einer Java-Anwendung zu finden ist – das Diagramm scheint, bis auf die Namensgebung, austauschbar zu sein. Dabei verzichten wir jedoch darauf, andere spezifische Merkmale der beiden Umgebungen zu betrachten, die bei einer genaueren Analyse auch eine Rolle spielen würden. Vielleicht ist es Ihnen nicht aufgefallen, aber ich habe die Worte ».NET-Anwendung« und »Java-Anwendung« benutzt – eine kleine Nuance mit weitreichender Konsequenz. Eine Java-Anwendung ist, darauf weist schon der Name hin, mit der Programmiersprache Java entwickelt worden; eine .NET-Anwendung hingegen ist nicht sprachgebunden. Sicher, in diesem Buch werden wir uns mit Visual C# beschäftigen, aber es macht praktisch keinen Unterschied, ob die Anwendung in Visual C# 2010, in Visual Basic 2010 oder F# entwickelt worden ist. Ausschlaggebend ist am Ende des Kompiliervorgangs nur ein kompatibler IL-Code, ungeachtet der zugrunde liegenden Sprache. Um sprachunabhängigen Code erzeugen zu können, muss es Richtlinien geben, an die sich alle .NET-Sprachen halten müssen, um ein Fiasko zu vermeiden. Diese Richtlinien, in denen die fundamentalen Eigenschaften einer .NET-kompatiblen Sprache festgelegt sind, werden durch die Common Language Specification (CLS) beschrieben. Die Common Language Specification ist ein offener Standard. Das hatte schon frühzeitig zur Folge, dass lange vor der offiziellen Einführung von .NET viele Softwareunternehmen andere Sprachen, beispielsweise Delphi, Eiffel und Cobol auf .NET portiert haben. Wenn alle Sprachen tatsächlich gleichberechtigt sind und dasselbe Ergebnis liefern, stellt sich natürlich die Frage, warum es zukünftig nicht nur eine Sprache gibt. Sogar Microsoft bietet mit C#, F#, C++ und VB .NET im Visual Studio vier verschiedene Sprachen an. Der Grund ist recht einfach: Man möchte den Entwicklern nicht eine vollkommen neue Sprache aufzwingen, sondern ihnen die gewohnte sprachspezifische Syntax lassen. Hier klicken, um das Bild zu vergrößern Abbildung 1.2 Die Common Language Specification als Basis der Sprachunabhängigkeit Wenn Sie nun anmerken sollten, dass es sich bei C# um eine völlig neue Sprache handelt, die mit der Veröffentlichung des .NET Frameworks zur Verfügung gestellt worden ist, haben Sie vollkommen recht. Allerdings assoziiert bereits der Name C# unzweifelhaft, dass die Wurzeln dieser Sprache in C/C++ zu finden sind. Die Konsequenzen, die sich aus der CLS ergeben, sind weitreichend – nicht für den Endanwender, den es nicht im geringsten interessiert, in welcher Sprache seine Applikation entwickelt wird, sondern vielmehr für ein heterogenes Entwicklerteam in einem Softwareunternehmen. Die Entscheidung, eine Anwendung auf der Grundlage von .NET zu entwickeln, ist keine Entscheidung für oder gegen eine Sprache – es ist eine konzeptionelle Festlegung. Die Bedeutung der einzelnen Sprachen rückt in den Hintergrund, denn die Komponenten, die in einer .NET-konformen Sprache geschrieben sind, können problemlos miteinander interagieren. Eine Klasse, die in C# geschrieben ist, kann von einer Klasse in Visual Basic 2010 beerbt werden. Beide Klassen können Daten miteinander austauschen und Ausnahmen weiterreichen. Es gibt unter .NET keine bevorzugte Programmiersprache. ### 1.2.5 Das Common Type System (CTS) Jede Entwicklungsumgebung beschreibt als eines ihrer wichtigsten Merkmale ein Typsystem, in dem einerseits Datentypen bereitgestellt werden und andererseits Vorschriften definiert sind, nach denen ein Entwickler die standardmäßigen Typen durch eigene erweitern kann. Darüber hinaus muss auch eine Regelung getroffen werden, wie auf die Typen zugegriffen wird. Mit dem Common Type System (CTS) der .NET-Plattform wird die sprachübergreifende Programmentwicklung spezifiziert und sichergestellt, dass Programmcode unabhängig von der zugrunde liegenden Sprache miteinander interagieren kann. Damit legt das Common Type System die Grundlage für die im vorhergehenden Abschnitt erläuterte Sprachunabhängigkeit. Alle Typen, die unter .NET zur Verfügung gestellt werden, lassen sich in zwei Kategorien aufteilen: Wertetypen Referenztypen Wertetypen werden auf dem Stack abgelegt. Zu ihnen gehören die in der Entwicklungsumgebung eingebauten ganzzahligen Datentypen und die Datentypen, die Fließkommazahlen beschreiben. Referenztypen werden hingegen auf dem Heap abgelegt. Zu ihnen gehören unter anderem die aus den Klassen erzeugten Objekte. Obwohl Wertetypen im ersten Moment nicht den Anschein erwecken, dass sie von der .NET-Laufzeitumgebung als Objekte behandelt werden, ist dies kein Widerspruch zu der Aussage von vorhin, dass .NET nur Objekte kennt. Tatsächlich erfolgt zur Laufzeit eine automatische Umwandlung von einem Werte- in einen Referenztyp durch ein Verfahren, das als Boxing bezeichnet wird. Typen können ihrerseits Mitglieder enthalten: Felder, Eigenschaften, Methoden und Ereignisse. Dem Common Type System nur die Festlegung von Typen zuzuschreiben, würde die vielfältigen Aufgaben nur vollkommen unzureichend beschreiben. Das CTS gibt zudem die Regeln vor, nach denen die Sichtbarkeit dieser Typmitglieder festgelegt wird. Ein als öffentlich deklariertes Mitglied eines vorgegebenen Typs könnte beispielsweise über die Grenzen der Anwendung hinaus sichtbar sein; andere Sichtbarkeiten beschränken ein Mitglied auf die aktuelle Anwendung oder sogar nur auf den Typ selbst. Das vom Common Type System festgelegte Regelwerk ist grundsätzlich nichts Neues. Alle anderen Sprachen, auch die, die nicht auf .NET aufsetzen, weisen ein ähnliches Merkmal auf, um ein Typsystem in die Sprache zu integrieren. Aber es gibt einen entscheidenden Unterschied, durch den sich alle Sprachen der .NET-Umgebung vom Rest abheben: Während die Definition des Typsystems bei herkömmlichen Sprachen Bestandteil der Sprache selbst ist, wandert das .NET-Typsystem in die Laufzeitumgebung. Die Folgen sind gravierend: Kommunizieren zwei Komponenten miteinander, die in unterschiedlichen Sprachen entwickelt worden sind, sind keine Typkonvertierungen mehr notwendig, da sie auf demselben Typsystem aufsetzen. Stellen Sie sich vor, es würde keine Regelung durch das CTS geben und C# würde einen booleschen Typ definieren, der 2 Byte groß ist, während C++ .NET denselben Datentyp definiert, jedoch mit einer Größe von 4 Byte. Der uneingeschränkte Informationsaustausch wäre nicht möglich, sondern würde zu einem Merkmal der Sprache degradiert. Im gleichen Moment würde das ansonsten sehr stabile Framework wie ein Kartenhaus in sich zusammenbrechen – eine fundamentale Stütze wäre ihm entzogen. Dieses Dilemma ist nicht unbekannt und beschert anderen Sprachen große Schwierigkeiten dabei, Funktionen der WinAPI-32 direkt aufzurufen. Ein Beispiel für diese Sprachen ist Visual Basic 6.0. ### 1.2.6 Das .NET Framework Ein Framework ist ein Gerüst, mit dem Anwendungen entwickelt, kompiliert und ausgeführt werden. Es setzt sich aus verschiedenen Richtlinien und Komponenten zusammen. Sie haben in Abschnitt 1.2.4 mit der Common Language Specification (CLS) und in Abschnitt 1.2.5 mit dem Common Type System (CTS) bereits einen Teil des .NET Frameworks kennengelernt. Wir müssen aber dieses Anwendungsgerüst noch um zwei sehr wichtige Komponenten ergänzen: die Common Language Runtime (CLR) die .NET-Klassenbibliothek Sie können in manchen Veröffentlichungen noch weitere Komponentenangaben finden, beispielsweise ADO.NET und ASP.NET. Es ist wohl mehr eine Sache der Definition, wo die Grenzen eines Frameworks gesetzt werden, da sich dieser Begriff nicht mit einer klar umrissenen Definition beschreiben lässt. Die .NET-Klassenbibliothek ihrerseits stellt einen Oberbegriff dar, unter dem sich sowohl ADO.NET als auch ASP.NET eingliedern lassen. ### 1.2.7 Die Common Language Runtime (CLR) Die Common Language Runtime (CLR) ist die Umgebung, in der die .NET-Anwendungen ausgeführt werden – sie ist gewissermaßen die allen gemeinsame Laufzeitschicht. Der Stellenwert dieser Komponente kann nicht hoch genug eingestuft werden, denn mit ihren Fähigkeiten bildet die CLR den Kern von .NET. Die CLR ist ein Verwalter – auf Englisch manager. Tatsächlich wird der Code, der in der Common Language Runtime ausgeführt wird, auch als verwalteter Code bezeichnet – oder im Englischen als managed code. Umgekehrt kann mit Visual Studio 2010 auch unverwalteter Code geschrieben werden. In unverwaltetem oder unmanaged code sind beispielsweise Treiberprogramme geschrieben, die direkt auf die Hardware zugreifen und deshalb plattformabhängig sind. Sie müssen sich die Common Language Runtime nicht als eine Datei vorstellen, der eine bestimmte Aufgabe im .NET Framework zukommt, wenn verwalteter Code ausgeführt wird. Vielmehr beschreibt die CLR zahlreiche Dienste, die als Bindeglied zwischen dem verwalteten IL-Code und der Hardware den Anforderungen des .NET Frameworks entsprechen und diese sicherstellen. Zu diesen Diensten gehören: der Class Loader, um Klassen in die Laufzeitumgebung zu laden der Type Checker, der unzulässige Typkonvertierungen unterbindet der JITter, um den MSIL-Code zur Laufzeit in nativen Code zu übersetzen, der im Prozessor ausgeführt werden kann der Exception Manager, der die Ausnahmebehandlung unterstützt der Garbage Collector, der eine automatische Speicherbereinigung anstößt, wenn Objekte nicht mehr benötigt werden der Code Manager, der die Ausführung des Codes verwaltet die Security Engine, die sicherstellt, dass der User über die Berechtigung verfügt, den angeforderten Code auszuführen die Debug Machine zum Debuggen der Anwendung der Thread Service zur Unterstützung multithreading-fähiger Anwendungen der COM Marshaller, der die Kommunikation mit COM-Komponenten (COM = Component Object Model) sicherstellt Die Liste ist zwar lang, vermittelt aber einen Einblick in die verschiedenen unterschiedlichen Aufgabenbereiche der Common Language Runtime. ### 1.2.8 Die .NET-Klassenbibliothek Das .NET Framework, das inzwischen in der Version 4.0 vorliegt, ist ausnahmslos objektorientiert ausgerichtet. Für Entwickler, die sich bisher erfolgreich dem objektorientierten Konzept widersetzt und beharrlich auf prozeduralen Code gesetzt haben (solche gibt es häufiger, als Sie vielleicht vermuten), fängt die Zeit des Umdenkens an, denn an der Objektorientierung führt unter .NET kein Weg mehr vorbei. Alles im .NET Framework wird als Objekt betrachtet. Dazu zählen sogar die nativen Datentypen der Common Language Specification wie der Integer. Die Folgen sind weitreichend, denn schon mit einer einfachen Deklaration wie
    int iVar;
erzeugen wir ein Objekt mit allen sich daraus ergebenden Konsequenzen. Wir werden darauf in einem der folgenden Kapitel noch zu sprechen kommen. Die .NET-Klassen stehen nicht zusammenhangslos im Raum, wie beispielsweise die Funktionen der WinAPI-32, sondern stehen ausnahmslos in einer engen Beziehung zueinander, der .NET-Klassenhierarchie. Eine Klassenhierarchie können Sie sich wie einen Familienstammbaum vorstellen, in dem sich, ausgehend von einer Person, alle Nachkommen abbilden lassen. Auch die .NET-Klassenhierarchie hat einen Ausgangspunkt, gewissermaßen die Wurzel der Hierarchie: Es ist die Klasse Object. Jede andere Klasse des .NET Frameworks kann darauf zurückgeführt werden und erbt daher deren Methoden. Außerdem kann es weitere Nachfolger geben, die sowohl die Charakteristika der Klasse Object erben als auch die ihrer direkten Vorgängerklasse. Auf diese Weise bildet sich eine mehr oder weniger ausgeprägte Baumstruktur. Für Visual C++-Programmierer ist eine Klassenhierarchie nichts Neues, sie arbeiten bereits seit vielen Jahren mit den MFC (Microsoft Foundation Classes). Auch Java-Programmierer haben sich an eine ähnliche Hierarchie gewöhnen müssen. Eine Klassenhierarchie basiert auf einer Bibliothek, die strukturiert ihre Dienste zum Wohle des Programmierers bereitstellt und letztendlich die Programmierung vereinfacht. Um allerdings in den Genuss der Klassenbibliothek zu kommen, ist ein erhöhter Lernaufwand erforderlich. Wenn man aber aus dieser Phase heraus ist, kann man sehr schnell und zielorientiert Programme entwickeln. Die anfänglichen Investitionen zahlen sich also schnell aus. Einen kurzen Überblick über den Inhalt der .NET-Klassenbibliothek zu geben, ist schwer, wenn nicht sogar vollkommen unmöglich, denn es handelt sich dabei um einige Tausend vordefinierte Typen. Wenn man sich jetzt vorstellt, dass in jeder Klasse mehr oder weniger viele Methoden definiert sind, also Funktionen im prozeduralen Sinne, dann kommt man sehr schnell in Größenordnungen von einigen Zehntausend Methoden, die insgesamt von den Klassen veröffentlicht werden. Alle zu kennen dürfte nicht nur an die Grenze der Unwahrscheinlichkeit stoßen, sondern diese sogar deutlich überschreiten. Außerdem kann man davon ausgehen, dass im Laufe der Zeit immer weitere Klassen mit immer mehr zusätzlichen und verfeinerten Features in die Klassenhierarchie integriert werden – sowohl durch Microsoft selbst als auch durch Drittanbieter. ### 1.2.9 Das Konzept der Namespaces Da jede Anwendung von Funktionalitäten lebt und der Zugriff auf die Klassenbibliothek zum täglichen Brot eines .NET-Entwicklers gehört, ist ein guter Überblick über die Klassen und insbesondere deren Handling im Programmcode sehr wichtig. Hier kommt uns ein Feature entgegen, das die Arbeit deutlich erleichtert: die Namespaces. Ein Namespace ist eine logische Organisationsstruktur, die völlig unabhängig von der Klassenhierarchie eine Klasse einem bestimmten thematischen Gebiet zuordnet. Damit wird das Auffinden einer Klasse, die bestimmte Leistungsmerkmale aufweist, deutlich einfacher. Das Konzept ist natürlich auch nicht ganz neu. Ob Java wieder Pate gestanden hat, wissen wir nicht. Aber in Java gibt es eine ähnliche Struktur, die als Package bezeichnet wird. Dass das Auffinden einer bestimmten Klasse erleichert wird, ist nur ein Argument, was für die Namespaces spricht. Einem zweiten kommt eine ebenfalls nicht zu vernachlässigende Bedeutung zu: Jede Klasse ist durch einen Namen gekennzeichnet, der im Programmcode benutzt wird, um daraus möglicherweise ein Objekt zu erzeugen und auf dessen Funktionalitäten zuzugreifen. Der Name muss natürlich eindeutig sein, schließlich können Sie auch nicht erwarten, dass ein Brief, der nur an Hans Fischer adressiert ist, tatsächlich den richtigen Empfänger erreicht. Namespaces verhindern Kollisionen zwischen identischen Klassenbezeichnern, sind also mit der vollständigen Adressierung eines Briefes vergleichbar. Nur innerhalb eines vorgegebenen Namespace muss ein Klassenname eindeutig sein. Die Namespaces sind auch wieder in einer hierarchischen Struktur organisiert. Machen Sie aber nicht den Fehler, die Klassenhierarchie mit der Hierarchie der Namespaces zu verwechseln. Eine Klassenhierarchie wird durch die Definition der Klasse im Programmcode festgelegt und hat Auswirkungen auf die Fähigkeiten einer Klasse, bestimmte Operationen ausführen zu können, während die Zuordnung zu einem Namespace keine Konsequenzen für die Fähigkeiten eines Objekts einer Klasse hat. Dass Klassen, die einem bestimmten Namespace zugeordnet sind, auch innerhalb der Klassenhierarchie eng zusammenstehen, ist eine Tatsache, die aus den Zusammenhängen resultiert, ist aber kein Muss. Wenn die Aussage zutrifft, dass Namespaces in einer baumartigen Struktur organisiert werden, muss es auch eine Wurzel geben. Diese heißt im .NET Framework System. Dieser Namespace organisiert die fundamentalsten Klassen in einen Verbund. Weiter oben habe ich erwähnt, dass sogar die nativen Datentypen wie der Integer auf Klassendefinitionen basieren – im Namespace System ist diese Klasse neben vielen weiteren zu finden. (Falls Sie die Klasse jetzt aus Neugier suchen sollten – sie heißt nicht Integer, sondern Int32). Unterhalb von System sind die anderen Namespaces angeordnet. Sie sind namentlich so gegliedert, dass man schon erahnen kann, über welche Fähigkeiten die einem Namespace zugeordneten Klassen verfügen. Damit Sie ein Gefühl hierfür bekommen, sind in Tabelle 1.1 auszugsweise ein paar Namespaces angeführt.

Tabelle 1.1 Auszug aus den Namespaces des .NET Frameworks

Namespace | Beschreibung
System.Collections | Klassen, die Auflistungen beschreiben
System.Data | Enthält die Klassen, um über ADO.NET auf Datenbanken zuzugreifen.
System.Drawing | Klassen, die grafische Funktionalitäten bereitstellen
System.IO | Klassen für Ein- und Ausgabeoperationen
System.Web | Enthält Klassen, die im Zusammenhang mit dem Protokoll HTTP stehen.
System.Windows.Forms | Enthält Klassen, um Windows-basierte Anwendungen zu entwickeln.
Die Tabelle gibt kaum mehr als einen Bruchteil aller .NET-Namespaces wieder. Sie sollten allerdings erkennen, wie hilfreich diese Organisationsstruktur bei der Entwicklung einer Anwendung sein kann. Wenn Sie die Lösung zu einem Problem suchen, kanalisieren die Namespaces Ihre Suche und tragen so zu einer effektiveren Entwicklung bei. Wir können in diesem Buch natürlich nicht alle Namespaces, geschweige denn alle Klassen des .NET Frameworks behandeln. Ob das überhaupt jemals ein Buch zu leisten vermag, darf mehr als nur angezweifelt werden – zu umfangreich ist die Klassenbibliothek.