Kapitel 3 Die .NET-Laufzeitumgebung
In der Vergangenheit war es schwierig, Module zu
schreiben, die aus mehreren Sprachen aufgerufen werden konnten. Code,
der in Visual Basic geschrieben wurde, kann nicht aus Visual C++ aufgerufen werden. Visual C++-Code kann unter
Umständen aus Visual Basic aufgerufen werden, aber dies ist
nicht einfach. Visual C++ verwendet die C- und C++-Laufzeitumgebungen,
die ein besonderes Verhalten aufweisen, und Visual Basic verwendet
eine eigene Ausführungsengine, die ebenfalls über ein eigenes und anderes
Verhalten verfügt.
Daher wurde COM entwickelt und hat sich als nützliche Methode
zum Schreiben von komponentenbasierter Software herausgestellt. Unglücklicherweise ist es
nicht leicht, COM in der Visual C++-Umgebung einzusetzen, außerdem
wird COM von Visual Basic nicht vollständig unterstützt.
Aus diesem Grund wurde COM häufig zum Schreiben von COM-Komponenten
eingesetzt, zur Entwicklung von systemeigenen Anwendungen dagegen weniger.
Wenn also ein Programmierer brauchbaren C++-Code schrieb und ein anderer Programmierer dies in Visual Basic
tat, gab es nicht wirklich die Möglichkeit zur Zusammenarbeit.
Darüber hinaus bestanden Schwierigkeiten bei
den Bibliotheksprovidern, denn es gab nicht einen, der in allen Umgebungen
eingesetzt werden konnte. Wenn die Bibliothek auf Visual Basic
ausgerichtet war, war die Verwendung in Visual Basic einfach, der
Zugriff von C++ war jedoch möglicherweise eingeschränkt oder
führte zu einer inakzeptablen Leistungseinbuße. Oder eine
Bibliothek wurde für C++-Benutzer geschrieben und bot diesen gute
Leistungsergebnisse und Lowlevelzugriff, für Visual Basic-Programmierer war sie jedoch nicht zugänglich.
Einige Bibliotheken wurden für beide Benutzertypen
geschrieben, aber dies bedeutete üblicherweise auch, Kompromisse
einzugehen. Beim Versenden einer E-Mail auf einem Windows-System hat
der Programmierer die Auswahl zwischen CDO (Collaboration Data Objects), einer COM-basierten Schnittstelle, die von beiden
Sprachen aus aufgerufen werden kann, jedoch nicht sämtliche Funktionen
bereitstellt1 , und systemeigenen MAPI-Funktionen (sowohl in den C- als auch in den C++-Versionen),
mit denen auf alle Funktionen zugegriffen werden kann.
Die .NET-Laufzeitumgebung wurde entwickelt, um hier Abhilfe zu schaffen.
Es gibt eine Möglichkeit, Code (Metadaten), eine Laufzeitumgebung
und eine Bibliothek (die Common Language Runtime und Frameworks) zu beschreiben. Das folgende Diagramm zeigt den
Aufbau der .NET-Laufzeitumgebung.
Die Common Language Runtime stellt die grundlegenden
Ausführungsdienste zur Verfügung. Die darüber angeordneten
Basisklassen stellen maßgebliche Datentypen, Auflistungsklassen
und allgemeine Klassen bereit. Oberhalb der Basisklassen befinden sich
die Klassen zur Handhabung von Daten und XML. Ganz oben in der Architektur befinden sich die
Klassen zur Offenlegung der Webdienste2 sowie die Klassen zur Handhabung der Benutzerschnittstelle. Eine
Anwendung kann von allen diesen Ebenen aus aufgerufen werden sowie die
Klassen sämtlicher Ebenen verwenden.
Zum Verständnis der Funktionsweise von C# sind
Kenntnisse über die .NET-Laufzeitumgebung und deren Frameworks
erforderlich. Die folgenden Abschnitte enthalten einen Überblick
über dieses Thema, detailliertere Informationen finden Sie in Kapitel 31,
C# im Detail.
3.1 Die Ausführungsumgebung
 
Dieser Abschnitt war ursprünglich mit »Die
Ausführungsengine« betitelt, aber die .NET-Laufzeitumgebung
ist viel mehr als nur eine Engine. Die Umgebung bietet ein einfacheres
Programmiermodell, Schutz und Sicherheit, Unterstützung für
leistungsstarke Tools sowie Methoden zum Bereitstellen, Packen usw.
3.1.1 Ein einfacheres Programmiermodell
 
Alle Dienste werden über ein gemeinsames Modell
bereitgestellt, auf das von allen .NET-Sprachen aus gleichberechtigt
zugegriffen werden kann. Alle Dienste können in einer beliebigen
.NET-Sprache geschrieben werden3 . Die Umgebung ist in hohem Maße spracherkennend und ermöglicht daher eine Sprachauswahl. Dies
macht eine Codewiederverwendung einfacher, sowohl für den Programmierer als
auch für den Bibliotheksprovider.
Die Umgebung unterstützt außerdem die
Verwendung von vorhandenem C#-Code, entweder über das Aufrufen von Funktionen
in DLLs oder dadurch, dass COM-Komponenten wie .NET-Laufzeitkomponenten eingesetzt werden.
.NET-Laufzeitkomponenten können auch in Situationen verwendet werden,
in denen COM-Komponenten benötigt werden.
Im Gegensatz zu den verschiedenen Fehlerbehandlungsmethoden vorhandener Bibliotheken werden in der .NET-Laufzeitumgebung
alle Fehler über Ausnahmen ermittelt. Es besteht also keine Veranlassung,
zwischen Fehlercodes, HRESULTs und Ausnahmen zu wechseln.
Abschließend enthält die Umgebung BCLs (Base Class Libraries), über die Funktionen bereitgestellt werden,
die traditionell in Laufzeitbibliotheken vorhanden sind, plus einige
neue Funktionen. Zu den BCL-Funktionen zählen:
|
Auflistungsklassen, z. B. Warteschlangen, Arrays, Stacks und Hashtabellen |
|
Klassen für den
Datenbankzugriff |
|
E/A-Klassen |
|
WinForms-Klassen
zum Erstellen der Benutzerschnittstelle |
|
Netzwerkklassen |
Außerhalb der Laufzeitumgebung für die
Basisklassen sind viele weitere Komponenten zur Behandlung der Benutzerschnittstelle
sowie zur Durchführung anderer aufwendiger Operationen vorhanden.
3.1.2 Sicherheit
 
Die .NET-Laufzeitumgebung bietet Schutz und Sicherheit.
Es handelt sich um eine verwaltete Umgebung, d. h. die Laufzeitumgebung
verwaltet den Speicher für den Programmierer. Der Programmierer
muss weder Speicher zuweisen noch diese Speicherzuweisungen wieder aufheben,
all dies wird durch die Speicherbereinigung erledigt. Durch diese wird
der Programmierer nicht nur entlastet, in einer Serverumgebung kann so auch die Anzahl der Speicherlecks erheblich
reduziert werden. Dies vereinfacht die Entwicklung von Systemen, bei
denen eine hohe Verfügbarkeit gefordert ist.
Zusätzlich bietet die .NET-Laufzeitumgebung eine Umgebungsprüfung. Zur Laufzeit wird über die Umgebung geprüft,
ob der Ausführungscode typensicher ist. Auf diese Weise werden
Fehler wie beispielsweise das Übergeben eines falschen Typs an
eine Funktion, das Überschreiten zugewiesener Bereiche bei einem
Lesevorgang oder das Ausführen von Code an ungeeigneten Standorten
ermittelt.
Das Sicherheitssystem interagiert mit dem Prüfcode, um sicherzustellen, dass über den Code nur
die Ausführung erfolgt, die diesem zugedacht ist. Die Sicherheitsanforderungen für einen bestimmten Codeabschnitt können
genau festgelegt werden. So kann beispielsweise angegeben werden, dass
ein Codeabschnitt in eine Arbeitsdatei schreiben muss; diese Anforderung
wird während der Ausführung überprüft.
3.1.3 Unterstützung für leistungsfähige
Tools
 
Microsoft stellt vier .NET-Sprachen bereit: Visual Basic, Visual C++ mit verwalteten Erweiterungen, C# und JScript. Andere Firmen arbeiten an Compilern für weitere
Sprachen, hierbei reicht das Spektrum von COBOL bis Perl.
Die Fehlersuche wurde in der .NET-Laufzeitumgebung
erheblich verbessert. Das gemeinsame Ausführungsmodell vereinfacht eine sprachübergreifende Fehlersuche,
die Fehlerermittlung kann sich mühelos über Code erstrecken,
der in verschiedenen Sprachen geschrieben wurde, unterschiedliche Prozesse
ausführt oder auf verschiedenen Computern läuft.
Des Weiteren sind alle .NET-Programmieraufgaben in der Visual Studio-Umgebung zusammengefasst, d. h., Entwurf, Entwicklung,
Fehlersuche und Bereitstellung von Anwendungen werden von hier aus realisiert.
3.1.4 Bereitstellen, Packen und weitere Funktionen
 
Die .NET-Laufzeitumgebung bietet auch auf diesen
Gebieten Hilfe. Die Bereitstellung von Anwendungen wurde vereinfacht,
in einigen Fällen fällt der traditionelle Installationsschritt
vollständig weg. Da die Pakete in einem allgemeinen Format bereitgestellt
werden, kann ein einzelnes Paket in einer beliebigen Umgebung ausgeführt
werden, die .NET unterstützt. Darüber hinaus trennt die Umgebung
die Anwendungskomponenten, sodass eine Anwendung nur mit den Komponenten
läuft, mit denen sie geliefert wurde und nicht mit anderen Versionen,
die zum Lieferumfang anderer Anwendungen gehören.
3.2 Metadaten
 
Metadaten sind das, was die .NET-Laufzeitumgebung
zusammenhält. Metadaten stellen das Äquivalent zu den Typbibliotheken
in der COM-Umgebung dar, bieten jedoch umfassendere Informationen.
Für jedes Objekt der .NET-Laufzeitumgebung
werden in den Metadaten alle Objektinformationen aufgezeichnet, die
zur Verwendung des Objekts erforderlich sind. Hierzu zählen:
|
Der Name des Objekts |
|
Die Namen aller Felder
des Objekts sowie deren Typen |
|
Die Namen aller Mitgliedsfunktionen,
einschließlich Parametertypen und -namen |
Auf diese Weise verfügt die .NET-Laufzeitumgebung
über ausreichende Informationen zur Erstellung der Objekte, zum
Aufrufen der Mitgliedsfunktionen oder für den Zugriff auf die Objektdaten,
und der Compiler kann mit diesen Informationen ermitteln, welche Objekte
verfügbar sind und welche sich in Verwendung befinden.
Diese Vereinheitlichung kommt sowohl dem Entwickler
als auch dem Codebenutzer zugute: Der Codeentwickler kann auf einfache
Weise Code erstellen, der in allen .NET-kompatiblen Sprachen verwendet werden kann, der Benutzer des Codes kann
Objekte verwenden, die von anderen Programmierern erstellt wurden, unabhängig
von der Sprache, in der die Objekte implementiert sind.
Zusätzlich ermöglichen Metadaten anderen
Tools den Zugriff auf detaillierte Codeinformationen. Die Visual Studio-Shell verwendet diese Informationen im Objektbrowser
und für Funktionen wie beispielsweise IntelliSense.
Darüber hinaus können – in einem
Prozess, der als Reflektion bezeichnet wird – mit Hilfe des Laufzeitcodes
die Metadaten abgefragt werden, um zu ermitteln, welche Objekte verfügbar
und welche Funktionen und Felder für die Klasse vorhanden sind.
Dies ähnelt der Verwendung von IDispatch in der COM-Umgebung,
es wird jedoch ein einfacheres Modell angewendet. Natürlich ist
ein derartiger Zugriff nicht stark typisiert, daher erfolgt eine Referenzierung
der Metadaten bei der Mehrzahl der Softwarekomponenten eher zur Kompilierungs-
als zur Laufzeit, aber für Anwendungen wie Skriptsprachen stellt
dies eine sehr nützliche Funktion dar.
Der Endbenutzer kann außerdem über die
Reflektion das Aussehen von Objekten ermitteln, nach Attributen suchen
oder Methoden ausführen, deren Namen bis zur Laufzeit nicht bekannt
sind.
3.3 Assemblierung
 
In der Vergangenheit konnte ein fertiges Softwarepaket
über verschiedene Mechanismen bereitgestellt werden, beispielsweise
als ausführbare Datei, DLL und LIB-Datei oder als DLL mit COM-Objekt und einer Typbibliothek.
In der .NET-Laufzeitumgebung wird als Packmethode die Assemblierung verwendet. Wird der Code über einen
der .NET-Compiler kompiliert, wird er in eine Zwischenform konvertiert,
die als »IL« bezeichnet wird. Die Assemblierung enthält
alle IL-, Metadaten- und weitere für die Ausführung erforderliche
Dateien in einem einzigen Paket. Jede Assemblierung enthält eine
Festlegung, in der die Dateien aufgeführt werden, die in der Assemblierung
enthalten sind. Über die Festlegung wird gesteuert, welche Typen
und Ressourcen außerhalb der Assemblierung offen gelegt werden
und mit dem Verweis auf Typen und Ressourcen den Dateien zugeordnet
werden, die die Typen und Ressourcen enthalten. In der Festlegung werden
außerdem die Assemblierungen aufgelistet, von denen eine Assemblierung
abhängt.
Assemblierungen sind unabhängig, d. h.,
sie enthalten genügend Informationen, um selbstbeschreibend zu
sein.
Die Definition einer Assemblierung kann in einer
einzigen Datei enthalten oder über mehrere Dateien verteilt sein.
Die Verwendung mehrerer Dateien ermöglicht das Herunterladen von
Abschnitten einer Assemblierung nach Bedarf.
3.4 Sprachinteroperabilität
 
Eines der Ziele der .NET-Laufzeitumgebung ist die
Spracherkennung, die Möglichkeit, Code in beliebiger Sprache zu
schreiben und zu verwenden. Es ist nicht nur möglich, in Visual Basic
geschriebene Klassen aus C# oder C++ (oder einer anderen .NET-Sprache) aufzurufen, sondern
eine Klasse, die in Visual Basic geschrieben wurde, kann auch als
Basisklasse für eine C#-Klasse eingesetzt werden. Diese Klasse
könnte dann wiederum von einer C++-Klasse eingesetzt werden.
Mit anderen Worten, es spielt keine Rolle, in welcher
Sprache eine Klasse erstellt wurde. Dazu ist zu sagen, dass häufig
gar nicht ermittelbar ist, in welcher Sprache eine Klasse geschrieben
wurde.
In der Praxis stößt man jedoch auf ein
paar Hindernisse. Einige Sprachen verfügen über Datentypen
ohne Vorzeichen, die nicht von anderen Sprachen unterstützt werden,
einige Sprachen unterstützen die Operatorüberladung. Die Erhaltung
der Ausdrucksvielfalt dieser sehr funktionellen Sprachen und die gleichzeitige
Gewährleistung der Klasseninteroperabilität mit anderen Sprachen stellt eine Herausforderung
dar.
Die .NET-Laufzeitumgebung bietet ausreichende Unterstützung
für Sprachen mit vielfältigen Funktionen4 , daher wird der Code, der in diesen Sprachen geschrieben wurde,
nicht durch die einfacheren Sprachen in seiner Funktionalität eingeschränkt.
Damit Klassen von der .NET-Laufzeitumgebung allgemein
verwendbar sind, müssen die Klassen den so genannten Common
Language Specifications (CLS) entsprechen, über die beschrieben wird, welche
Funktionen in einer öffentlichen Schnittstelle der Klasse sichtbar sind (alle Funktionen können
klassenintern verwendet werden). Die CLS verbieten beispielsweise das
Offenlegen von Datentypen ohne Vorzeichen, da diese nicht von allen
Sprachen verwendet werden können. Weitere Informationen zu den
CLS finden Sie im .NET-SDK im Abschnitt zur sprachübergreifenden Interoperabilität.
Ein Benutzer, der C#-Code schreibt, kann angeben,
dass dieser CLS-konform sein sollte. Der Compiler markiert in diesem Fall
alle nicht konformen Bereiche. Weitere Informationen zu den spezifischen
Einschränkungen für C#-Code hinsichtlich der CLS finden Sie
im Abschnitt »CLS-Kompatibilität« in Kapitel 31, C# im Detail.
3.5 Attribute
 
Zur Umwandlung einer Klasse in eine Komponente sind
häufig weitere Informationen erforderlich, beispielsweise Informationen
dazu, wie eine Klasse auf Festplatte gespeichert werden kann oder wie
Transaktionen gehandhabt werden sollten. Der traditionelle Ansatz
besteht darin, die Informationen in eine separate Datei zu schreiben
und zum Erstellen einer Komponente die Datei mit dem Quellcode zu kombinieren.
Das Problem bei diesem Ansatz liegt darin, dass
Informationen häufig dupliziert werden. Dieses Vorgehen ist mühselig
und fehleranfällig, außerdem verfügen Sie erst dann über
die vollständige Komponente, wenn Sie beide Dateien besitzen5 .
Die .NET-Laufzeitumgebung unterstützt benutzerdefinierte Attribute (diese werden in C# einfach als Attribute
bezeichnet), durch die beschreibende Informationen zusammen mit einem
Objekt in den Metadaten gespeichert werden können. Die Daten können
dann zu einem späteren Zeitpunkt abgerufen werden. Attribute bieten
einen allgemeinen Mechanismus hierfür und werden in der Laufzeitumgebung
häufig zum Speichern von Informationen bei der Änderung der
Klassenverwendung eingesetzt. Attribute sind vollständig erweiterbar,
wodurch der Programmierer Attribute definieren und verwenden kann.
1
Dies rührt wahrscheinlich daher, dass es schwierig
ist, das interne Lowleveldesign in etwas zu übersetzen, das von
einer Automatisierungsschnittstelle aufgerufen werden kann.
2
Eine Möglichkeit zur Offenlegung einer programmatischen
Schnittstelle über einen Webserver.
3
Einige Sprachen sind möglicherweise nicht mit
den systemeigenen Plattformfunktionen kompatibel.
4
Dies trifft für das verwaltete C++ nicht ganz
zu, denn im Vergleich zu C++ müssen einige Einschränkungen
hingenommen werden.
5
Jeder, der jemals versucht hat, eine COM-Programmierung
ohne Typbibliothek durchzuführen, kennt dieses Problem.
|