(0) exportieren Drucken
Alle erweitern
Erweitern Minimieren

Eiffel im Web: Integrieren von Eiffel-Systemen in das Microsoft .NET Framework

Veröffentlicht: 07. Sep 2000 | Aktualisiert: 09. Nov 2004
Von Raphael Simon, Emmanuel Stapf, Bertrand Meyer, und Christine Mingins

Dieser Artikel beschreibt die Implementierung und Integration der Eiffel- und Eiffel#-Systeme in das Microsoft .NET Framework, um eine Umgebung zu erstellen, die ambitionierten Entwicklern von Internetsoftware hervorragende Lösungen zur Verfügung stellt.

* * *

Auf dieser Seite

Einführung Einführung
 Erstellen von .NET Framework-Systemen mit Eiffel  Erstellen von .NET Framework-Systemen mit Eiffel
Ziele Ziele
Strategie Strategie
Eiffel#, die Sprache Eiffel#, die Sprache
Eiffel, eine kurze Einführung Eiffel, eine kurze Einführung
Eiffel# oder Eiffel? Eiffel# oder Eiffel?
Besonderheiten des Microsoft .NET Frameworks Besonderheiten des Microsoft .NET Frameworks
Einsetzen des .NET Framework Einsetzen des .NET Framework
Strategie Strategie
Der Emitter Der Emitter
Externe Klassen Externe Klassen
Eiffel#-Bibliotheken Eiffel#-Bibliotheken
Der .NET Framework Contract-Assistent Der .NET Framework Contract-Assistent
Schlussfolgerung Schlussfolgerung
Referenzmaterial Referenzmaterial

Einführung

Seit Sommer 1999 arbeiten Interactive Software Engineering (ISE) und Microsoft zusammen mit einer Gruppe der Monash University in Australien an der Integration von Eiffel in das Microsoft® .NET™ Framework.

Eiffel ist eine umfassende Umgebung zur Softwareentwicklung (ISE Eiffel) und basiert auf einer Methode, die den gesamten Lebenszyklus der Software berücksichtigt, d.h. Implementierung, Analyse, Design und Verwaltung. Die auf der Programmiersprache Eiffel basierende Umgebung folgt den Grundsätzen der Objekttechnologie und implementiert die Konzepte von Design by Contract, um extrem zuverlässige, erweiterbare und wieder verwendbare Anwendungen zu erstellen. ISE Eiffel eignet sich speziell zur Entwicklung großer und komplexer Systeme und wird von führenden Organisationen verschiedener Branchen (u.a. Finanz-, Verteidigungs-, Echtzeit- und andere Industrien) für wichtige Entwicklungen verwendet. Auch Universitäten auf der ganzen Welt (wie die Monash University) setzen Eiffel bei der Unterrichtung von Programmierern und Softwareingenieuren ein.

Das Microsoft .NET Framework stellt die nächste Generation der bei Microsoft entwickelten Webtechnologie dar, die zur Erstellung von Internetanwendungen viele technische Lösungen verwendet. Mit dem integrierten Active Server Pages+ (ASP+) können webbasierte Anwendungen schneller und einfacher als auf dem herkömmlichen Weg erstellt werden. Die Technologie zeichnet sich im Wesentlichen durch eine Laufzeit aus, die Bytecode (die interne Sprache des virtuellen Computers, auch IL genannt) mit Metadaten interpretiert und/oder kompiliert. Diese Metadaten beschreiben jede Systemkomponente einschließlich des Prototyps für sämtliche Methoden, Felder oder Ereignisse.

Eiffel im Microsoft .NET Framework stellt die ideale Kombination für Unternehmen dar, die die besten Technologien in Betriebssystemen, Internet- und Webinfrastrukturen, Methoden zur Softwareentwicklung sowie Entwicklungsumgebungen nutzen möchten. Speziell die Offenheit von Eiffel gegenüber anderen Sprachen und Umgebungen und die Sprachenneutralität von .NET Framework machen aus dem Endprodukt ein ideales Werkzeug zur Erstellung von Anwendungen, die Komponenten in vielen unterschiedlichen Sprachen enthalten, welche durch Eiffel zusammengehalten werden. In diesem Artikel beschreiben wir diese Kombination sowie die Schwierigkeiten bei der Integration von ISE Eiffel in das .NET Framework.

Erstellen von .NET Framework-Systemen mit Eiffel

Die Bestimmung eines Sprachcompilers für das .NET Framework bedeutet, dass man in der Lage sein muss, die IL und ihre Metadaten zu erstellen.

Ziele

Wollten wir einfach nur Eiffel im .NET Framework kompilieren, wäre die Generierung der IL ausreichend. Unser Ziel ist es jedoch, ein allgemeines Framework zur Unterstützung der mehrsprachigen Interoperabilität zu schaffen, da andere Sprachen nicht in der Lage wären, die Eiffel-Typen ohne ihre beschreibenden Metadaten wieder zu verwenden. Eines der Ziele von ISE im Hinblick auf die Integration von Eiffel ist die Möglichkeit, bestehende Typen wieder zu verwenden, die in einer beliebigen Sprache geschrieben wurden, sowie die Generierung von Typen, die von jeder anderen .NET Framework-Entwicklungsumgebung verstanden werden können. Eiffel erweitert das .NET Framework insofern, als dass Eiffel-Klassen aus Klassen anderer Sprachen geschrieben werden können, sich erweitern und wieder in IL kompilieren lassen und es somit anderen Umgebungen ermöglichen, den neuen Typ wieder zu verwenden.

Diese Generierung der IL und ihrer Metadaten wird über einen besonderen Switch in der Systemdatei ausgeführt (die Datei, die das Eiffel-System beschreibt, auch ACE-Datei genannt), so dass dieser neue Eiffel-Compiler vollständig in EiffelBench, die integrierte Entwicklungsumgebung (IDE) von ISE, integriert werden kann. Eiffel-Programmierer werden auch nach der Integration in der Lage sein, auf gewohnte Weise weiterzuarbeiten.

Ein weiterer Anschlussaspekt liegt in der Integration des Compilers in Microsoft Visual Studio® .NET. Diese Integration verhilft neuen Eiffel-Entwicklern, die sich aus verschiedenen Gründen nicht mit dem Erlernen einer neuen Umgebung befassen können oder möchten, aber bereits mit Visual Studio arbeiten, zu einer kürzeren Lernkurve. Die Integration unterstützt sämtliche Funktionen von Visual Studio wie die Syntaxmarkierung, den Debugger sowie weitere Assistenten.

Ein weiteres Ziel ist die Möglichkeit, ASP+-Seiten in Eiffel zu schreiben. ISE möchte eine vollständige Unterstützung (die sog. erweiterte Semantik) erreichen, so dass Eiffel-Entwickler in der Lage sind, Eiffel in ASP+-Seiten einzubetten, indem sie die "@language="Eiffel""-Direktive verwenden. Diese Unterstützung bezieht sich auch auf die Möglichkeit, Webdienste in Eiffel zu schreiben.

Strategie

Unter Berücksichtigung der genannten Ziele hat ISE zwei wichtige Meilensteine für die Integration festgelegt. Der erste Schritt bei der Integration besteht in der Erstellung einer neuen Sprache, genannt Eiffel# (sprich "Eiffel Sharp").
Diese Teilmenge von Eiffel wurde speziell für das .NET Framework konzipiert und soll das gesamte Konzept von Design by Contract aufnehmen. Sie ist leistungsstark genug, um reale Anwendungen zu erstellen und gleichzeitig das ursprüngliche Objektmodell des .NET Framework zu bewahren. In einem zweiten Schritt werden die Ergebnisse erweitert, um das komplette Eiffel-Objektmodell zu umfassen.

Sowohl die Integration in Visual Studio als auch in ASP+ wurde mit Eiffel# begonnen. Die Integration in Visual Studio begann mit einem einfachen Wrapping um den Befehlszeilencompiler, wobei nicht alle verfügbaren Funktionen mit eingeschlossen wurden. Die Unterstützung von ASP+ wurde mit der Unterstützung von Webdiensten begonnen.

Die vollständige Unterstützung dieser Technologien und der gesamten Eiffel-Sprache wird mit der ersten Nichtbetaversion von Microsoft .NET Framework zur Verfügung stehen. Eiffel# wird die .NET Framework-Technologien schrittweise verstärkt verwenden.

Eiffel#, die Sprache

Dieser Abschnitt behandelt die Festlegung der ersten Version von Eiffel#. Genauere Angaben werden mit den weiteren Betaversionen von .NET Framework folgen. Die wichtigste Anforderung an Eiffel# ist die ausschließliche Verwendung der gemeinsamen Sprachlaufzeit ohne jegliche Eiffel-Laufzeiten.

Eiffel, eine kurze Einführung

Da sich der Rest dieses Dokuments mit der Definition von Eiffel# anhand der Unterschiede zu Eiffel befasst, sollten wir zunächst die wichtigsten Merkmale von Eiffel kennen lernen. Ausführlichere Informationen finden Sie in den Büchern Object-Oriented Software Construction, 2nd edition und Eiffel: The Language sowie in der Eiffel-Website (in Englisch) unter http://www.eiffel.com/, aus denen einiges Material entnommen wurde.

Eiffel ist eine "rein" objektorientierte Sprache (wohl die semantischste Anwendung objektorientierter Grundsätze unter den bestehenden Sprachen) und basiert auf einigen wenigen leistungsstarken Konzepten:

  • Nahtlose Entwicklung. Eiffel kann für den gesamten Lebenszyklus, angefangen von Analyse und High-level-Design bis zur Implementierung und Verwaltung, als einzelnes konzeptuelles Framework im Softwareprozess eingesetzt werden.

  • Klassen bilden die einzige Grundlage für die Modulstruktur und das Typsystem.

  • Vererbung von Klassifizierung, Subtypen und Wiederverwendung.

  • Sorgfältige und effektive Methode der multiplen Vererbung (Umbenennung, Auswahl, erneute Definition, Aufhebung der Definition, wiederholte Vererbung).

  • Prüfpunkte zum Schreiben korrekter und robuster Software, Debuggen und automatischen Dokumentieren.

  • Geregelte Ausnahmenbearbeitung zur erfolgreichen Wiederherstellung bei fehlerhaften Fällen.

  • Statische Eingabe ohne Schleifenlöcher im Typsystem sorgt für Sicherheit.

  • Dynamische Bindung aus Flexibilitäts- und Sicherheitsgründen.

  • Beschränkte und unbeschränkte Generizität zur Beschreibung flexibler Containerstrukturen.

  • Offene Architektur für leichten Zugriff auf Software, die in anderen Sprachen geschrieben wurde, z.B. C, Microsoft Visual C++® und andere.

Die nachfolgende Übersicht einer einfachen ZÄHLER-Klasse zur Beschreibung eines Zählers bringt Ihnen die Syntax und den Stil der Sprache näher:

<span class=cfe>indexing</span> 
               <I>Beschreibung: "Zähler, die sich um jeweils eins erhöhen,</I> 
<I>               vermindern und zurücksetzen lassen."</I>  
<span class=cfe>class</span> 
               <I>ZÄHLER</I> 
<span class=cfe>feature</span> -- Zugriff 
<I>Element</I>: <I>GANZZAHL</I> 
                      -- Zählerwert. 
<span class=cfe>feature</span> -- Elementänderung 
<I>Erhöhen</I> <span class=cfe>is</span> 
                      -- Zähler um eins erhöhen. 
          <span class=cfe>do</span> 
                    <I>Element</I> := <I>Element</I> + <I>1</I> 
          <span class=cfe>end</span> 
<I>Vermindern</I> <span class=cfe>is</span> 
                     -- Zähler um eins vermindern. 
          <span class=cfe>do</span> 
                    <I>Element</I> := <I>Element</I> - <I>1</I> 
          <span class=cfe>end </span> 
<I>Zurücksetzen</I> <span class=cfe>is</span> 
                      -- Zähler auf Null zurücksetzen. 
          <span class=cfe>do</span> 
                     <I>Element</I> := 0 
          <span class=cfe>end</span> 
<span class=cfe>end</span> -- Klasse <I>ZÄHLER</I> 

Zur Laufzeit wird diese Klasse Instanzen aufweisen, wobei jede Instanz ein Objekt ist, das einen einzelnen Zähler darstellt. Wenn Sie einen Zähler erstellen möchten, müssen Sie die entsprechende Einheit angeben,

<I>mein_Zähler</I>: <I>ZÄHLER</I>

das entsprechende Objekt erstellen (der Befehl zur Objekterstellung lautet "create")

<span class=cfe>create</span> <I>mein_Zähler</I> 

und den Zähler anschließend auf die Operationen der Klasse (ihre Funktionen) anwenden:

<I>mein_Zähler</I>.<I>erhöhen</I> 
... 
<I>mein_Zähler</I>.<I>vermindern</I> 
... 
<I>print</I> (<I>mein_Zähler</I>.<I>Element</I>)  

Solche Operationen werden in den Funktionen anderer Klassen auftreten, auch Klienten der ZÄHLER-Klasse genannt. Noch einige Anmerkungen zu diesem Beispiel: Alle Werte werden standardmäßig initialisiert, so dass der Lebenszyklus jedes Zählerobjekts mit seinem Wert und Element, auf Null initialisiert (Sie müssen zu Anfang kein Zurücksetzen aufrufen), beginnt. Außerdem ist jedes Element ein Attribut, das in den schreibgeschützten Modus exportiert wird. Klienten können "print" verwenden (print mein_Zähler.Element), allerdings nicht im Beispiel von mein_Zähler .Element:= 657, was eine Verletzung der "Informationsausblendung" bedeuten würde. Selbstverständlich kann der Klassenautor eine solche Möglichkeit zur Verfügung stellen, indem er eine Funktion hinzufügt:

<I>set</I> (<I>beliebiger_Wert</I>: <I>GANZZAHL</I>) <span class=cfe>is</span> 
                 -- Wert des Zählers auf beliebiger_Wert setzen. 
     <span class=cfe>do</span> 
                 <I>Element</I> := <I>beliebiger_Wert</I> 
     <span class=cfe>end </span> 

In diesem Fall verwenden die Klienten einfach mein_Zähler.set (657). Es liegt jedoch im Ermessen des Autors der ZÄHLER-Klasse, wie viele Funktionen er ihren Klienten zuweisen möchte. Die Indizierungsanweisung zu Beginn der Klasse hat zwar keine Auswirkung auf ihre Semantik (die Eigenschaften der entsprechenden Laufzeitobjekte), fügt der Klasse jedoch zusätzliche Dokumentationen hinzu.

Als einzige der Entwurfsmethodiken und -sprachen führt Eiffel das Design by Contractüber Konstrukte wie Klasseninvarianten, Vorbedingungen und Nachbedingungen direkt durch. Nehmen wir beispielsweise an, unsere Zähler sollten immer größer als Null sein. In diesem Fall verfügt die Klasse nun über eine Invariante:

<span class=cfe>indexing ... class 
</span>        <I>ZÄHLER</I> 
<span class=cfe>feature </span> 
         ... 
<span class=cfe>invariant</span> 
        <I>Element</I> >= <I>0</I> 
<span class=cfe>end</span> 

Für die Funktionsverminderung ist jetzt eine Vorbedingung erforderlich, damit die Klienten keine unzulässigen Operationen ausführen. Das Schlüsselwort "require" führt die Vorbedingung ein:

<I>vermindern</I> <span class=cfe>is</span> 
             -- Zähler um eins vermindern. 
     <span class=cfe>require</span> 
             <I>Element</I> > <I>0</I> 
     <span class=cfe>do</span> 
            <I>Element</I> := <I>Element</I> - <I>1</I> 
     <span class=cfe>ensure</span> 
            <I>Element</I> = <span class=cfe>old</span> <I>Element</I> - <I>1</I> 
     <span class=cfe>end</span> 

Das Schlüsselwort "ensure" führt die Nachbedingung ein.

Die Vorbedingung warnt den Klienten: "Wage es nicht, mich aufzurufen, wenn du dir nicht sicher bist, ob der Zähler zwingend einen positiven Wert haben muss." Die Nachbedingung besagt: "Wenn du brav bist (und die Vorbedingung befolgst), dann verspreche ich dir Folgendes: Ich werde den Zähler um eins vermindern."

Die Invariante fügt noch folgende Versprechung hinzu: "Außerdem sorgen alle meine Operationen dafür, dass der Zähler einen positiven Wert bzw. den Wert Null beibehält." Vorbedingungen, Nachbedingungen und Invarianten werden Assertionen genannt.

Eiffel# oder Eiffel?

Diese kurze Einführung in Eiffel wirft einige interessante Fragen hinsichtlich der Integration in das Microsoft .NET Framework auf. Am schwierigsten gestaltet sich möglicherweise die Unterstützung der multiplen Vererbung, da die normale Sprachlaufzeit ausschließlich zur Unterstützung einer einfachen Vererbung konzipiert wurde. Da Eiffel# nur die normale Sprachlaufzeit verwenden darf, muss sie dem .NET Framework-Objektmodell folgen und kann somit die multiple Vererbung aktiver oder teilweise verzögerter Klassen nicht unterstützen. Sie könnten jedoch vollständig verzögerte Klassen erstellen, die in diesem Fall als Schnittstellen generiert würden. Die teilweise verzögerte oder aktive übergeordnete Klasse, falls vorhanden, ist der Ausgangstyp.

Die neuen Eiffel-Konstrukte, die nach der Veröffentlichung der aktuellen Ausgabe von Eiffel: The Language hinzugefügt wurden, werden von Eiffel# nicht unterstützt. Zu diesen Konstrukten zählen Agenten und verwandte Klassen, die generische Übereinstimmung und die generische Argumentenerstellung.

Darüber hinaus unterstützt die normale Sprachlaufzeit im Gegensatz zum Eiffel-Objektmodell keine Kovarianz. Aus diesem Grund kann auch Eiffel# keine Kovarianz unterstützen, d.h., Sie können die Typen der Argumente bzw. die Ergebnisse der Funktionen nicht in der untergeordneten Klasse neu definieren.

Der letzte Unterschied zwischen den beiden Sprachen liegt in der Semantik der erweiterten Typen. Diese werden im .NET Framework direkt mit den sog. Werttypen verbunden. Erweiterte Typen in Eiffel hingegen, obwohl nicht grundlegend anders als die Werttypen im .NET Framework, verhalten sich anders: Werttypen werden versiegelt, d.h. sie können nicht vererbt werden. Demzufolge können erweiterte Typen in Eiffel# ebenfalls nicht vererbt werden.

Zwischen Eiffel und Eiffel# bestehen keine Unterschiede. Eiffel# unterstützt vielmehr Contracts, Ausnahmenbearbeitung und Generizität, einige Markenzeichen von Eiffel.

Besonderheiten des Microsoft .NET Frameworks

Eiffel# verfügt außerdem über einige Sonderfunktionen zur Verwendung der notwendigen Funktionen von .NET Framework. Der erste wichtige Unterschied liegt im Anwendungspaket. In einer Standardumgebung und unter der Voraussetzung, eine DLL- oder eine EXE-Datei erstellen zu können, definiert das .NET Framework solche neuen Konzepte als Baugruppen und Module, die jeder Umgebungscompiler unterstützen sollte. Eine Baugruppe setzt sich aus einer Gruppe von Modulen zusammen und entspricht einer Anwendung. Ein Modul kann entweder eine DLL- oder eine EXE-Datei sein. Aus diesem Grund enthält die ACE-Datei von Eiffel# neue Optionen zur Beschreibung der verschiedenen Module, die Teil der Baugruppe sind. Der Eiffel#-Compiler generiert eine Baugruppe mit dem Namen des in der ACE-Datei angegebenen Systems. In der Standardoption

 il_generation 

können Sie wie folgt festlegen, ob es sich bei einer Baugruppe um eine EXE- oder eine DLL-Datei handeln soll:

   <span class=cfe>system</span> 
      <I>Beispiel</I> 
   <span class=cfe>root</span> 
<I>STAMM_KLASSE</I>: <I>"make"</I> 
   <span class=cfe>default</span> 
      <span class=cfe>il_generation</span> (<I>"exe"</I>) -- "dll" zur Generierung einer DLL 
   ... 

In diesem Beispiel generiert der Compiler eine einzelne Datei namens "Beispiel.exe", die die Baugruppe und das Modul enthält. Falls Sie unterschiedliche Dateien für mehrere Module festlegen möchten, können Sie die Option

 module 

für jedes Cluster verwenden und im Cluster die Option für jede Klasse überschreiben:

   <span class=cfe>system</span> 
      <I>Beispiel</I> 
   <span class=cfe>root</span> 
<I>STAMM_KLASSE</I>: <I>"make"</I> 
   <span class=cfe>default</span> 
      <span class=cfe>il_generation</span> ("exe") -- "dll" zur Generierung einer DLL 
   <span class=cfe>cluster</span> 
      <I>Stamm_cluster</I>: <I>"c:\meine_Anwendung"</I> 
         <span class=cfe>default</span> 
            <span class=cfe>module</span> (<I>"meine_Anwendung"</I>) 
         <span class=cfe>option</span> 
            <span class=cfe>module</span> (<I>"Stamm"</I>): <I>STAMM_KLASSE</I> 
         <span class=cfe>end</span> 
   ... 

Diese ACE-Datei definiert drei Module:

  • Das erste Modul enthält das Baugruppenmanifest und heißt "Beispiel.exe".

  • Das zweite Modul, "meine_Anwendung.dll", schließt alle Klassen – mit Ausnahme der Klasse STAMM_KLASSE – im Cluster Stamm_cluster ein.

  • Das letzte Modul , "Stamm.dll", enthält die Klasse STAMM_KLASSE. Dieser Mechanismus erlaubt Ihnen, so viele Module wie erforderlich zu definieren und die Klassen als Teil des Eiffel-Systems entsprechend Ihren Vorstellungen zu gruppieren.

Eine weitere spezielle Funktion von .NET Framework ist der Begriff des Namespaces. Jedem .NET Framework-Typ wird ein Namespace zugewiesen, der die Einmaligkeit des Typnamens im System sicherstellt. Mit der folgenden ACE-Standardoption können Sie für sämtliche Klassen des Eiffel-Systems einen Standardnamespace definieren:

   <span class=cfe>system</span> 
      <I>Beispiel</I> 
   <span class=cfe>root</span> 
<I>ROOT_CLASS</I>: <I>"make"</I> 
   <span class=cfe>default</span> 
      <span class=cfe>il_generation</span> ("exe") -- "dll" zur Generierung einer DLL 
      <span class=cfe>namespace</span> ("MeineAnwendung") 
   ... 

In diesem Beispiel werden alle Klassen des Eiffel-Systems im Namespace "MeineAnwendung.<Cluster_name>" generiert, wobei <Cluster_name> für den Namen des Clusters mit der Klasse steht. Bei Bedarf können Sie den Standardnamespace jedes Clusters wie folgt überschreiben:

   <span class=cfe>system</span> 
      <I>Beispiel</I> 
   <span class=cfe>root</span> 
<I>STAMM_KLASSE</I>: <I>"make"</I> 
   <span class=cfe>default</span> 
      <span class=cfe>il_generation</span> ("exe") -- "dll" zur Generierung einer DLL 
      <span class=cfe>namespace</span> ("MeineAnwendung") 
   <span class=cfe>cluster</span> 
      <I>Stamm_cluster</I>: <I>"c:\meine_Anwendung"</I> 
         <span class=cfe>default</span> 
            <span class=cfe>module</span> (<I>"meine_Anwendung"</I>) 
            <span class=cfe>namespace</span> ("Stamm") 
         <span class=cfe>option</span> 
            <span class=cfe>module</span> (<I>"Stamm"</I>): <I>STAMM_KLASSE</I> 
         <span class=cfe>end</span> 
   ... 

Bei dieser ACE-Datei werden sämtliche Klassen des Clusters Stamm_cluster im Namespace "Stamm" generiert. Beachten Sie, dass der in der Clusteranweisung angegebene Name nicht an den Namespace angehängt wird, der in der Standardanweisung definiert ist. Die Klassen von Eiffel# können zudem eine Aliasanweisung enthalten (eine Erklärung des "Alias"-Schlüsselworts finden Sie unter "Externe Anweisungen"). In diesem Fall überschreibt der in der Anweisung angegebene Name jeden Namespace, der in der ACE-Datei festgelegt ist.

Ein weiterer wichtiger Unterschied hängt mit der dynamischen Natur des .NET Framework zusammen und liegt im Laufzeitverhalten von Contracts. In einer herkömmlichen Umgebung resultiert eine Contract-Verletzung in einer Ausnahme, und die Ebene der Überprüfung durch die Assertionen wird beim Kompilieren festgelegt. Diese Methode ist im .NET Framework jedoch nicht erfolgreich durchführbar, da sich der Aufrufer (Caller) einer Contract-Funktion in einem anderen Modul befinden kann. Der Klient einer Contract-Komponente muss entscheiden können, auf welcher Ebene, falls vorhanden, die Contract-Überprüfung ausgeführt werden soll. Aus diesem Grund wird eine Standardschnittstelle verwendet, die die zur Verfügung stehenden Contract-Operationen definiert. Diese Schnittstelle namens IContract muss mit den Contract-Komponenten implementiert werden und definiert die Funktionen, mit denen die Ebenen der Contract-Überprüfung festgelegt werden:

  • Prüfen der Vorbedingungen: Dies ist die niedrigste Ebene; es werden nur die Vorbedingungen der Funktionen überprüft.

  • Prüfen der Nachbedingungen: Zwischenebene; es werden Vor- und Nachbedingungen überprüft.

  • Prüfen der Invarianten: Komplette Überprüfung aller Contracts (Vorbedingungen, Nachbedingungen und Invarianten).

Mit IContract können Sie zudem wählen, ob im Fall einer Contract-Verletzung eine Ausnahme ausgegeben werden soll.

<span class=cfe>interface</span> <I>IContract</I> 
{ 
   // Unterliegt Änderungen 
   <span class=cfe>public</span> <I>boolesche Vorbedingung_aktiviert</I>; 
   <span class=cfe>public</span><I> boolesche Nachbedingung_aktiviert</I>; 
   <span class=cfe>public</span><I> boolesche Invariante_aktiviert</I>; 
   <span class=cfe>public</span><I> void aktivieren_Vorbedingung</I>(); 
   <span class=cfe>public</span> <I>void</I> <I>aktivieren_Nachbedingung</I>(); 
   <span class=cfe>public</span> <I>void</I> <I>aktivieren_Invariante</I>(); 
   <span class=cfe>public</span> <I>void</I> <I>deaktivieren_Vorbedingung</I>(); 
   <span class=cfe>public</span> <I>void</I> <I>deaktivieren_Nachbedingung</I>(); 
   <span class=cfe>public</span> <I>void</I> <I>deaktivieren_Invariante</I>(); 
   <span class=cfe>public</span> <I>boolesche</I> <I>Ausnahme_bei_Verletzung</I>(); 
   <span class=cfe>public</span> <I>void</I> <I>aktivieren_Ausnahme_bei_Verletzung</I>(); 
   <span class=cfe>public</span> <I>void</I> <I>deaktivieren_Ausnahme_bei_Verletzung</I>(); 
   <span class=cfe>public</span> <I>ContractAusnahme</I> <I>letzte_Contract_Ausnahme</I>(); 
} 

Der Eiffel#-Compiler generiert automatisch eine Implementierung der Schnittstelle IContract mit der in der ACE-Datei angegebenen Standardebene zur Contract-Überprüfung:

   <span class=cfe>system</span> 
      <I>Beispiel</I> 
   <span class=cfe>root</span> 
<I>STAMM_KLASSE</I>: <I>"make"</I> 
   <span class=cfe>default</span> 
      <span class=cfe>il_generation</span> ("exe") 
      <span class=cfe>assertion</span> (require) -- Kann 'no', 'require', 'ensure' oder  
         'invariant' sein. 
   ... 

Wenn Sie die Assertionsoption auslassen, wird die Schnittstelle IContract nicht generiert. Wählen Sie die Option 'no', um IContract zu generieren. Contracts werden jedoch erst dann überprüft, wenn die Contract-Überprüfung von einem Klient aktiviert wird.

Einsetzen des .NET Framework

Sie haben erfahren, wie Sie mit Hilfe von Eiffel# .NET Framework-Komponenten erstellen können. Da der Compiler sämtliche notwendigen Metadaten generiert, können andere Sprachen die Eiffel#-Komponenten je nach Wunsch wieder verwenden (Erben oder Klientbeziehung). Die nächste Frage lautet nun, wie bestehende Komponenten in Eiffel# wieder verwendet werden. Zu bestehenden Komponenten zählen z.B. die Microsoft-Bibliotheken oder Komponenten, die von anderen Parteien geschrieben wurden.

Strategie

ISE stellt Eiffel#-Bibliotheken zum Wrappen des Microsoft-Frameworks bereit. Diese Bibliotheken enthalten Wrapper für die Basisklassenbibliothek, Win Forms und Web Forms. In der Basisklassenbibliothek finden Sie die Definition der für jedes System erforderlichen Grundtypen wie Auflistungen, Remotedienste, Threadingdienste, Sicherheit und E/A-Zugriff.

Die Win Forms-Klassen ermöglichen die Erstellung einer grafischen Benutzeroberfläche (GUI, Graphical User Interface) im .NET Framework. Sie agieren als Wrapper um die Win32-APIs, die ein leeres Objektmodell zur Erstellung einer GUI bereitstellt. Mit Web Forms können Sie außerdem GUIs im Web erstellen. Sie enthalten Typen wie DataGrid oder HTMLImage.

Diese drei Bibliotheken gehören zum Lieferumfang von Eiffel#, so dass Sie diese Klassen direkt in Ihrem System wieder verwenden können.

Der Emitter

Natürlich sind die Microsoft-Bibliotheken nicht die einzigen .NET Framework-Komponenten, auf die Sie zugreifen möchten. Ihr System erfordert u.U. die Integration Hunderter solcher Komponenten, die in einer Vielzahl von Sprachen geschrieben wurden. Aus diesem Grund stellt ISE ein Tool namens Emitter bereit, mit dem Sie jede .NET Framework-Baugruppe analysieren und einen Eiffel-Wrapper für jeden definierten Typ erstellen können. Der Emitter greift auf die Metadaten für jeden in der Baugruppe definierten Typ zu, verbindet sie mit dem jeweiligen Gegenstück in Eiffel und generiert die entsprechenden Klassen.

Obwohl eine Baugruppe Verweise auf andere Baugruppen (sog. externe Baugruppen) enthalten kann, generiert der Emitter nur Klassen für die Typen, die in der gegebenen Baugruppe definiert sind. Somit wird verhindert, dass z.B. Basisklassenbibliotheken für alle Baugruppen, die Sie wrappen möchten, generiert werden (da nahezu jede Baugruppe auf die Basisklassenbibliotheken verweist).

Da jeder öffentliche .NET Framework-Typ der Common Language Specification (CLS) entsprechen muss und sich diese CLS in bestimmten Punkten vom Eiffel-Modell unterscheidet, muss der Emitter einige bedeutende Änderungen vornehmen, um die .NET Framework-Typen in Eiffel-Klassen abzubilden. Der vielleicht wichtigste Unterschied liegt in der Überlastung der CLS. Das Eiffel-Modell untersagt eine Überlastung und verlangt, dass jede
überladene Funktion eindeutig gemacht wird. Der dabei verwendete Algorithmus lautet wie folgt:

f1, f2, ..., fn seien überladene NGWS-Funktionen mit demselben Namen 
   (n >= 2) 
Bei 1 <= i <= n, Si soll die Signatur von fi sein: 
   Si = [Ti1, Ti2, ..., Tim] 
      (im >= 0)    
Sämtliche Si unterscheiden sich auf Grund der Überladungsregeln. 
Wir legen fest, dass die Position u eindeutig ist für die Funktion fi (für 1 <= u <= im),  
   wenn es mindestens eine weitere Funktion fj (1 <= j <= n, j /= i) gibt, 
      so dass u <= jm und Tju /= Tiu. 
Wir bestimmten den eindeutigen Namen Ni für fi wie folgt. Ni hat das Format 
   N_Ti1_Ti2..._Tiu (0 <= u <= im), wobei [Ti1, Ti2, ... ,Tiu] die 
      kleinste ursprüngliche Subsequenz von Si und anders ist als die 
         entsprechende Subsequenz für alle anderen Funktionen. Auf Grund 
            der Überlastungsegeln ist eine solche Subsequenz  
               vorhanden und ein eindeutiges Merkmal von Si. 

Einfach ausgedrückt hängt der Algorithmus so viele Signaturtypnamen an den Funktionsnamen wie nötig, um einen eindeutigen Namen zu erhalten. Beispielsweise wird die folgende C#-Funktion

   <span class=cfe>public static</span> <I>void</I> <I>WriteLine</I> (<I>Zeichenfolge</I> <I>Format</I>, <I>Objekt</I> <I>arg0</I>); 
   <span class=cfe>public static</span> <I>void</I> <I>WriteLine</I> (<I>int</I> <I>Wert</I>); 
   <span class=cfe>public static</span> <I>void</I> <I>WriteLine</I> (<I>Zeichenfolge</I> <I>Wert</I>); 

vom Emitter folgendermaßen in Eiffel übersetzt:

   <I>WriteLine_System_Zeichenfolge_System_Objekt</I> (<I>Format</I>: <I>ZEICHENFOLGE</I>; <I>arg0</I>: <I>JEDES</I>) 
   <I>WriteLine_System_Int32</I> (<I>Wert</I>: <I>GANZZAHL</I>) 
   <I>WriteLine_System_Zeichenfolge</I> (<I>Wert</I>: <I>ZEICHENFOLGE</I>) 

Sicher haben Sie die Zuordnung der Grundtypen bemerkt, die der Emitter für die Argumenttypen durchführt. In der folgenden Tabelle sind sämtliche primitiven Typen mit ihrer CLS- und ihrer Eiffel-Definition aufgeführt:

Primitiver CLS-Typ (Beschreibung)

Eiffel-Typ

System.Char (2-Byte-Ganzzahl, unsigniert)

ZEICHEN

System.Byte (1-Byte-Ganzzahl, unsigniert)

GANZZAHL_8

System.Int16 (2-Byte-Ganzzahl, signiert)

GANZZAHL_16

System.Int32 (4-Byte-Ganzzahl, signiert)

GANZZAHL

System.Int64 (8-Byte-Ganzzahl, signiert)

GANZZAHL_64

System.Single (4-Byte-Zahl mit Gleitkomma)

REAL

System.Double (8-Byte-Zahl mit Gleitkomma)

DOPPELT

System.String (eine Zeichenfolge mit mindestens Null (zulässigen) Zeichen)

ZEICHENFOLGE

System.Object (der Stamm aller Klassenvererbungshierarchien)

JEDES

System.Boolean (True oder False)

BOOLESCH

In einigen wenigen Fällen kann der Emitter keinen gültigen Eiffel-Code generieren. Die generierte Klasse muss dann manuell bearbeitet werden. Glücklicherweise ist dies nicht oft der Fall (Sie müssen lediglich vier Klassen bearbeiten, wenn Sie alle Basisklassenbibliotheken, d.h. über 600 Typen, wrappen möchten). Das Hauptproblem, das diese fehlerhafte Generierung verursacht, liegt in der Verwendung des sog. MethodImpls. Dieser Mechanismus ermöglicht einem Typ, die Bindung einer seiner Schnittstellenfunktionen an eine Funktion mit einem anderen Namen oder einer anderen Signatur zu erzwingen. MethodImpls werden oftmals in Situationen verwendet, in denen die Kovarianz verwendet worden wäre, wenn sie in der Laufzeit der normalen Sprache zur Verfügung gestanden hätte. Leider ist es derzeit nicht möglich, über den Spiegelungsmechanismus von .NET Framework auf die Informationen von MethodImpls zuzugreifen.

Externe Klassen

Die vom Emitter generierten Eiffel#-Klassen enthalten keine Logik, sondern sind einfach für das Eiffel-Typsystem erforderlich. Das bedeutet, dass der Eiffel#-Compiler auch keinen IL-Code für diese Klassen generiert. Alle Aufrufe von Funktionen in vom Emitter generierten Klassen sind direkte Aufrufe des .NET Framework-Typ – es gibt keine Dereferenzierungen und daher keine Leistungseinschränkung. Damit der Compiler solche Klassen erkennen kann, hat ISE in Eiffel einen neuen Mechanismus, die sog. externen Klassen, implementiert. Diese Klassen können nur externe Merkmale enthalten (Merkmale, die nicht in Eiffel geschrieben wurden, aber Methoden oder Funktionen eines bereits vorhandenen .NET Framework-Typs darstellen). Alle Merkmale einer externen Klasse müssen Merkmale desselben .NET Framework-Typs sein. Einen solchen Typ können Sie mit folgender Syntax angeben:

<span class=cfe>frozen external class 
</span><I>   SYSTEM_KONSOLE</I> 
<span class=cfe>alias   </span> 
   <I>"System.Console"</I> 
... 

Die Zeichenfolge, die auf alias folgt, enthält den Namen des .NET Framework-Typs, der von der Eiffel-Klasse gewrappt wird. Da .NET Framework-Typen versiegelt werden können (d.h., sie können ihre Vererbung an andere Typen verhindern) und in Eiffel ein solches Konzept nicht existiert, wird in Eiffel# die neue Verwendung des Eiffel-Schlüsselworts "frozen" eingeführt. Sie können frozen vor external verwenden, um den Eiffel#-Compiler anzuweisen, dass der .NET Framework-Typ an keine Eiffel#-Klasse vererbt werden darf.

Die externe Klasse sollte anschließend alle Merkmale auflisten, auf die Sie zugreifen müssen. Es handelt sich dabei ausschließlich um externe Merkmale. Die Syntax eines externen .NET Framework-Merkmals sieht folgendermaßen aus:

<span class=cfe>frozen</span> <I>ReadLine</I>: <I>ZEICHENFOLGE</I> <span class=cfe>is</span> 
   <span class=cfe>external</span> 
      <I>"IL statische Signatur:System.String verwendet System.Console"</I> 
   <span class=cfe>alias</span> 
      <I>"ReadLine"</I> 
   <span class=cfe>end</span> 

Hier zeigt frozenan, dass das Merkmal u.U. nicht in einer untergeordneten Klasse neu definiert werden kann (Anmerkung: Sie können externe Merkmale ggf. neu definieren, wenn sie virtuell sind. In diesem Fall sollten Sie frozen nicht verwenden.) ReadLine lautet der Name des Eiffel#-Merkmals, und ZEICHENFOLGE ist der Rückgabetyp des Merkmals. Die Zeichenfolge nach dem Schlüsselwort "external" gibt die Art der externen .NET Framework-Funktionssignatur sowie den .NET Framework-Typ an, für den die Funktion definiert ist. Die Zeichenfolge nach dem Schlüsselwort "alias" enthält den .NET Framework-Namen der Funktion. Abhängig vom Typ oder der Methode, auf die sie Zugriff gewähren, gibt es unterschiedliche externe Merkmale. Eiffel# definiert sieben neue externe Merkmale, die nachfolgend aufgeführt sind:

Art der Microsoft .NET Framework-Funktion

Externes Eiffel-Merkmal

Methode

"IL-Signatur … Benutzerklassen_name"

Statische Methode

"IL-Signatur … statische Verwendung Klassen_name"

Feldabruf

"IL-Signatur … Feld Verwendung Klassen_name"

Abruf statischer Felder

"IL-Signatur … statisches_Feld Verwendung Klassen_name"

Feldfestlegung

"IL-Signatur … festlegen_Feld Verwendung Klassen_name"

Festlegung statischer Felder

"IL-Signatur … festlegen_statisches_Feld Verwendung Klassen_name"

Erstellung

"IL-Signatur … Ersteller Verwendung Klassen_name"

Bei Bedarf können Sie solche externen Merkmale in nicht externen Klassen definieren. Im Sonderfall der externen Klassen muss der Name des .NET Framework-Typs, der am Ende der Zeichenfolge nach dem Schlüsselwort "external" steht, mit dem Namen übereinstimmen, der auf das Schlüsselwort "alias" im Anschluss an die Angabe der Klasse folgt (siehe Codefragment unter "Externe Klassen").

Klienten oder untergeordnete Klassen können die externen Merkmale wie jedes andere Eiffel-Merkmal aufrufen. Falls Ihr System ein Merkmal beinhaltet, bei dem eine Benutzereingabe erforderlich ist, kann es folgenden Code enthalten:

   <I>erfordert_Benutzer_eingabe</I> <span class=cfe>is</span> 
         -- Benutzereingabe annehmen und etwas ausführen. 
      <span class=cfe>local</span> 
         <I>E/A</I>: <I>SYSTEM_KONSOLE</I> 
         <I>Eingabe</I>: <I>ZEICHENFOLGE</I> 
      <span class=cfe>do</span> 
         <span class=cfe>create</span> <I>E/A</I>.<I>make</I> 
         <I>Eingabe</I> := <I>E/A</I>.<I>ReadLine </I>-- aufrufen 
            <I>System.Console.ReadLine</I>() 
         -- etwas ausführen 
      <span class=cfe>end</span> 

Da ReadLine ein statisches externes Merkmal ist, brauchen Sie den Wrapper bei seinem Aufrufen nicht zu instanziieren. Der nachfolgende Code ist für den externen Aufruf ausreichend:

   <I>erfordert_Benutzer_eingabe</I> <span class=cfe>is</span> 
         -- Benutzereingabe annehmen und etwas ausführen. 
      <span class=cfe>local</span> 
         <I>E/A</I>: <I>SYSTEM_KONSOLE</I> 
         <I>Eingabe</I>: <I>ZEICHENFOLGE</I> 
      <span class=cfe>do</span> 
         -- Entfernt Erstellung von <I>E/A</I> 
         <I>Eingabe</I> := <I>E/A</I>.<I>ReadLine </I>-- aufrufen 
            <I>System.Console.ReadLine</I>() 
         -- etwas ausführen 
      <span class=cfe>end</span> 

Dieser Code wird auch für den Zugriff auf und die Festlegung von statischen Feldern verwendet. Bei anderen externen Merkmalen muss ein Wrapper instanziiert werden.

Eiffel#-Bibliotheken

Zum Lieferumfang des Eiffel#-Compilers gehört eine Eiffel#-Basisklassenbibliothek, die den Entwurfsgrundsätzen der EiffelBase-Standardbibliothek entspricht. Diese Bibliothek macht ausnehmenden Gebrauch von Generizität und Contracts, um eine Reihe reiner und leistungsstarker Datenstrukturen bereitzustellen, die Sie in Ihrem System wieder verwenden können. Diese Bibliothek verwendet die externen Klassen, die die ebenfalls zum Umfang von Eiffel# gehörende .NET Framework-Basisklassenbibliothek wrappen.

Die beiden anderen Compiler-Klassen wrappen die .NET Framework-Win Forms/Web Forms-Bibliotheken, so dass Sie GUI- und Webanwendungen leicht erstellen können.

Der .NET Framework Contract-Assistent

Im Rahmen dieser Entwicklung haben wir ein neues Tool, den Microsoft .NET Framework Contract-Assistenten, erstellt, der es dem Benutzer mittels des Metadatenmechanismus ermöglicht, Eiffel-ähnliche Contracts den .NET Framework-Komponenten aus verschiedenen Sprachen interaktiv hinzuzufügen. Dieses Tool wird ausführlich in einem eigenen Artikel erläutert werden, ist jedoch bereits für Entwickler verfügbar, die die Vorteile von Design by Contract auch in anderen Sprachen nutzen möchten. Diese wichtige Erweiterung wurde durch Metadateneinrichtungen von .NET Framework ermöglicht.

Schlussfolgerung

Ziel dieses Projekts und des daraus entstandenen Produkts ist die vollständige Integration zwischen ISE Eiffel und dem Microsoft .NET Framework. Die kombinierte Leistung der Plattform und der Entwicklungsumgebung sollte die ideale Umgebung zur Erstellung leistungsstarker Internetanwendungen ergeben, die die Öffentlichkeit heutzutage von uns erwartet. Die Verwendung von Eiffel im Microsoft .NET Framework ermöglicht Flexibilität, Produktivität und hohe Zuverlässigkeit. Die Vorzüge von Design by Contract in einer verteilten Umgebung, in der sich die nachträgliche Fehlersuche und -behebung als mühselig und teuer erweist, sind von enormem Wert. Gemeinsam mit den weiteren Vorteilen der Eiffel-Methode – nahtlose Entwicklung, generische Programmierung, Informationsausblendung und andere Grundsätze der Softwareentwicklung sowie ein leistungsstarker Vererbungsmechanismus – stellt der Einsatz von Eiffel im Microsoft .NET Framework eine hervorragende Lösung für ambitionierte Entwickler von Internetsoftware dar.

Referenzmaterial

Meyer, Bertrand. Eiffel: The Language. Prentice Hall, 1992.

Meyer, Bertrand. Object-Oriented Software Construction, 2nd edition. Prentice Hall, 1997.

ISE Eiffel Website (in Englisch) unter http://www.eiffel.com/


Anzeigen:
© 2014 Microsoft