次の方法で共有


Web 上の Eiffel: Eiffel システムの Microsoft .NET Framework への統合

Raphael Simon
Emmanuel Stapf
Bertrand Meyer
Interactive Software Engineering
Santa Barbara, California

Christine Mingins
Monash University
Melbourne, Australia

概要: この記事は、意欲的なインターネット ソフトウェアの開発者にとって、最善のソリューションを提供する環境を作り出す Microsoft .NET Framework への Eiffel および Eiffel# システムの実装と統合について説明しています。

目次

はじめに
Eiffel からの .Net Framework システムの作成
Eiffel# 言語とは
.NET Framework の利用
むすび
参考文献

はじめに

ISE (Interactive Software Engineering) は Microsoft® .NET™ Framework に Eiffel を統合するために、オーストラリアのモナッシュ大学のグループと共に 1999年夏以来、Microsoft と提携しています。

Eiffel は、実装だけでなく、分析、設計、および保守を含むソフトウェアのライフサイクル全体をカバーするメソッドに基づく、総合ソフトウェア開発環境 (ISE Eiffel) です。この環境は Eiffel 言語を元にしています。Eiffel 言語は、オブジェクト テクノロジの原理を全面的に適用し、信頼性の高い、拡張可能で再利用可能なアプリケーションを作成するために、契約による設計 (Design by Contract) の概念を実装しています。ISE Eiffel は、特に大規模で複雑なシステムに適合され、金融、防衛、リアルタイム、およびその他の業界の主要な組織で、重要な開発のために使用されています。モナッシュ大学など世界中の大学では Eiffel を使用して、あらゆるレベルでプログラミングとソフトウェア エンジニアリングの教育を行っています。

Microsoft .NET Framework は Microsoft で開発され、インターネット アプリケーションを構築するための多くの技術的なソリューションを利用する、次世代の Web テクノロジです。フレームワークは、従来のアプローチよりもかなり高速で容易な方法で Web ベースのアプリケーションを構築するための ASP+ (Active Server Pages+) を含んでいます。このテクノロジの中核はメタデータを使用してバイト コードを解釈、コンパイルするランタイムで構成されています。このバイト コードは、仮想マシンの内部コードで、"IL" としても知られています。メタデータは、すべてのメソッド、フィールド、またはイベントのプロトタイプを含めて、システムの各コンポーネント部分をて記述します。

Microsoft .NET Framework 上の Eiffel は、オペレーティング システム、インターネットおよび Web インフラストラクチャ、ソフトウェア開発方法、および開発環境で最善のテクノロジを利用したい企業にとって、理想的な組み合わせを提供します。特に Eiffel のほかの言語や環境に対するオープン性と .NET Framework が組み合わされ、言語中立性が強化され、結果として作成される製品を、多くの異なる言語内のコンポーネントを含むアプリケーションを構築する理想的な媒体にします。その際、Eiffel はそれらをつなぎ合わせる役目をします。この記事では、ISE Eiffel を .NET Framework と統合する際に直面した組み合わせと挑戦について説明しています。

Eiffel からの .Net Framework システムの作成

.NET Framework に言語コンパイラを対象させることは IL と関連するメタデータを作成できることを意味します。

目標

「.NET Framework で Eiffel をコンパイルする」ことだけが目的であれば、IL を生成することで充分ですが、Eiffel 型を記述しているメタデータがないと、ほかの言語が Eiffel 型を再利用できないので、多言語の相互運用性のための汎用のフレームワークを提供するという目標には不十分です。ISE が Eiffel の統合に関して掲げている目標の 1 つにほかの .NET Framework 開発環境でも認識できる型の作成だけでなく、ほかの言語で記述されている既存の型を再利用することがあります。Eiffel は .NET Framework "エクステンダ" なので、ほかの言語で記述されたクラスから継承する Eiffel クラスを記述し、IL に再コンパイルできます。これは、ほかの環境での新しい型の再利用を可能にしています。

この IL とメタデータの生成は、システム ファイル内の特別なスイッチを使用して行われます。システム ファイルは、Eiffel システムを説明するファイルで ACE ファイルとも呼ばれます。新しい Eiffel コンパイラは、ISE の統合開発環境 (IDE)、EiffelBench に完全に統合されています。既存の Eiffel プログラマは、統合以前と同じ方法で作業できます。

移植のもうひとつの側面として、コンパイラは Microsoft Visual Studio® .NET に統合するように構成されています。この統合により、新しい環境について学びたくない、または学ぶ時間がない新しい Eiffel の開発者でも、Visual Studio を使用した経験があれば学習時間を短縮する手助けになります。この統合は、構文の強調表示、デバッガ、およびいくつかのウィザードなどの Visual Studio 固有の機能をすべてサポートします。

もうひとつの主要な目標は Eiffel で ASP+ ページを記述する機能です。ISE は、フル サポート ("高度なセマンティクス" とも呼ばれます) を目指しています。そのため、Eiffel 開発者は


  "@language="Eiffel"" 

ディレクティブを使用して、Eiffel を ASP+ ページに埋め込むことができるようになります。このサポートには Web サービスを Eiffel で記述する機能も含まれます。

計画

これらの目標を念頭において、ISE は統合の 2 つの主要なマイルストーンを設定しました。統合の最初の段階では Eiffel# ("エッフェル シャープ" と発音します) という新しい言語が作成されます。この Eiffel のサブセットは特別に .NET Framework を対象に設計され、契約による設計 (Design by Contract) の完全な拡張を具体化しています。.NET Framework のネイティブなオブジェクト モデルを保持したままで、実際のアプリケーションを作成できる十分な機能を備えています。次の段階では、Eiffel の完全なオブジェクト モデルを包含する言語に拡張します。

Visual Studio と ASP+ への統合は、共に Eiffel# を使用して開始されました。Visual Studio への統合は、すべての使用可能な機能を含まないコマンドライン コンパイラを簡単にラップすることから開始されました。ASP+ のサポートは、Web サービスのサポートすることから開始されました。

Eiffel 言語全体と、これらのすべてのテクノロジの完全なサポートは、Microsoft .NET Framework の最初のベータ版以降のリリースで利用可能になる予定です。Eiffel# は、より多くの .NET Framework テクノロジを応用するように発展していきます。

Eiffel# 言語とは

このセクションでは Eiffel# の最初のバージョンを定義することに焦点を当てます。.Net Framework の今後のベータ版では、仕様は進化しているでしょう。Eiffel# の主要な必要条件は、ほかの Eiffel 固有のランタイムを使用せずに、Common Language Runtime だけを使用することです。

Eiffel の簡単なご紹介

このドキュメントの残りの部分では、Eiffel# が Eiffel とどのように異なっているか説明することによって Eiffel# を定義しているので、まず Eiffel の主な特性について見ておく必要があります。詳細については、Eiffel の Web サイト http://www.eiffel.com/ と、『Object-Oriented Software Construction, 2nd edition 』および『Eiffel: The Language,』という書籍を参照してください。

言語として Eiffel は、少数の優れた概念に基づいた "純粋な" オブジェクト指向言語です。間違いなく、既存の言語の中でオブジェクト指向原理に基づく最も体系的なアプリケーションです。

  • シームレスな開発。Eiffel は、分析および高度な設計、実装、保守までのライフサイクル全体に適応可能で、ソフトウェア プロセス全体を通して 1 つの総合的なフレームワークを提供します。

  • モジュール構造と型システムの両方の唯一の基盤としての役割を果たすクラス。

  • クラス化、サブタイプ化、すなわち再利用のための継承。

  • 多重継承に対する念入りで、効果的なアプローチ (名前の変更、選択、再定義、未定義、繰り返される継承)

  • 正しく、強固なソフトウェアを記述し、デバッグし、自動的に文書化するためのアサーション

  • 異常な状態から回復するための統制のとれた例外処理。

  • 安全性のために、型システム内に抜け道のない静的な型付け。

  • 柔軟性と安全性のための動的バインディング。

  • 柔軟性のあるコンテナ構造を記述するための制約付きおよび制約付きでないジェネリック クラス。

  • C、Microsoft Visual C++® などの、ほかの言語で記述されたソフトウェアへの容易なアクセスを提供するオープン アーキテクチャ。

以下は、明確で簡単な Eiffel 言語の構文およびスタイルを理解するために、ここではカウンタについて記述する簡単な COUNTER クラスの概要を示します。

indexing
               description:
      "Counters that you can increment by
      one,
               decrement,
      and reset"
      
class
               COUNTER

feature
      -- アクセス

item:
      INTEGER
                     
      -- カウンタの値。

feature -- 要素の変更

increment
      is
                     
      -- カウンタを 1
      増加します。
         
      do
                  
      item := item +
      1
         
      end

decrement
      is
                     --
      カウンタを 1 減少します。
         
      do
                  
      item := item -
      1
          end
      

reset
      is
                     
      -- カウンタをリセットして 0
      にします。
         
      do
                     item
      := 0
         
      end

end -- クラス COUNTER 

実行時に、このクラスはインスタンスを作成します。各インスタンスは、個別のカウンタを表す 1 つのオブジェクトです。カウンタを作成するには、対応するエンティティを宣言し、


  my_counter: COUNTER 

対応するオブジェクトを作成します。ここで create はオブジェクト作成操作です。

create my_counter 

その後、クラスの操作 (その機能) をカウンタに適用します。

my_counter.increment
...
my_counter.decrement
...
print
      (my_counter.item) 

このような操作は、COUNTER クラスのクライアントとなるほかのサブクラスの機能にもあります。この例について、もう少し説明を加えます。すべての値は既定値で初期化されます。そのため、すべてのカウンタ オブジェクトは値 item が 0 に初期化されます。はじめに reset を呼び出す必要はありません。item は読み取り専用モードでエクスポートされる属性です。クライアントは print (my_counter.item) と記述できますが、たとえば、my_counter .item := 657 とは記述できません。"情報隠ぺい" 違反になります。もちろん、クラスの作成者は、機能を追加することにより、このような能力を提供することも可能です。

set (some_value: INTEGER)
      is
               
      -- カウンタの値を some_value に設定します。
   
      do
               
      item := some_value
    end

この場合、クライアントは単に my_counter.set (657) を使用します。ただし、それはクライアントにどれだけの機能を提供するかをクラス COUNTER の作成者が判断します。クラスの先頭にある indexing 句は意味的な (対応するランタイム オブジェクトのプロパティ) 影響は与えませんが、クラスの追加のドキュメンテーションを添付します。

Eiffel は、設計の方法論と言語だけではなく、クラスの不変条件、事前条件、および事後条件などの構成要素全体に渡って契約による設計 (Design by Contract) を直接設定します。たとえば、カウンタが常に負の値にならないようにすると仮定します。そうすると、クラスは不変条件を 1 つ持つようになります。

indexing ...
      class
      
      COUNTER
feature
      
         ...
invariant
      
      item >= 0
end 

decrement 機能は、クライアントが無効な操作をしないように事前条件を必要とします。キーワード "require" は事前条件を示します。

decrement
      is
            
      -- カウンタを 1 減少します。
   
      require
            
      item > 0
   
      do
            item
      := item - 1
   
      ensure
            item
      = old item - 1
    end
      

キーワード "ensure" は事後条件を示します。

事前条件は、クライアントに「カウンタが確実に正の数であるとき以外は、呼び出さないよう」指示しています。事後条件は、「事前条件を守っている場合は、確実にカウンタを 1 減少させる」ことを明示しています。

また、不変条件は「カウンタを正の数または 0 に維持する」ことを保証します。事前条件、事後条件、および不変条件はアサーションと呼ばれています。

Eiffel# と Eiffel

この簡単な Eiffel のご紹介は、Microsoft .NET Framework への統合に関する興味深い問題を提起します。Common Language Runtime は単一継承のみをサポートするように設計されたので、最も挑戦的なことは多重継承のサポートです。Eiffel# は、Common Language Runtime を使用する必要があるので、.NET Framework オブジェクト モデルに従う必要があります。そのため有効なクラス、または部分遅延クラスの多重継承を不可能にしています。ただし、純粋遅延クラスの多重継承は可能です。この場合、クラスはインターフェイスとして生成されます。部分遅延クラスまたは有効な親クラスがある場合は、それらはベース型になります。

Eiffel# は、現在のエディション Eiffel: The Language の公開以降に追加された新しい Eiffel 構成要素はサポートしません。新しい構成要素には、エージェントと関連クラス、ジェネリック クラス、およびジェネリック引数生成を含みます。

Common Language Runtime と Eiffel オブジェクト モデルの間に生じる 2 つ目の違いは、共通ランタイムで共分散のサポートが不足していることです。このため、Eiffel# は共分散 (Covariance) をサポートしません。つまり、引数の型または子孫クラスの機能の結果を再定義できません。

2 つの言語の 3 つ目の違いは、拡張型のセマンティックスにあります。.NET Framewok の拡張型は、"値型" と呼ばれるものに直接マップされています。基本的には同じですが Eiffel の拡張型と .NET Framework の値型は完全には同じ動作をしません。特に、値型はシールドです。つまり、値型からは継承できません。その結果として Eiffel# で拡張された型から継承できません。

これ以外には Eiffel と Eiffel# の異なる点はありません。実際 Eiffel# は、コントラクト、例外処理、および Eiffel プログラミングの顕著な特徴のいくつかの汎用性をサポートします。

Microsoft .NET Framework の仕様

Eiffel# には、.NET Framework の必要な側面を応用するいくつか固有の機能もあります。最初の重要な違いは、アプリケーションのパッケージングです。標準の環境では、DLL または EXE を作成する選択肢があれば十分ですが、.NET Framework はアセンブリとモジュールという新しい概念を定義します。アセンブリとモジュールは、コンパイラがサポートする必要があります。アセンブリは、モジュールのグループで構成され、1 つのアプリケーションに対応しています。モジュールは DDL または EXE のどちらかになります。このため Eiffel# の ACE ファイルは、アセンブリの一部になる異なるモジュールを記述する新しいオプションを導入します。Eiffel# コンパイラは、ACE ファイルで指定されている system の名前を持つ 1 つのアセンブリを生成します。アセンブリが EXE または DLL のどちらであるかを、以下のように


  il_generation 

default オプションで指定できます。

   system
      sample
   root
ROOT_CLASS:
      "make"
   default
      il_generation
      ("exe") -- DLL を生成するには "dll" を使用します。
   ...
      

この例では、コンパイラはアセンブリとモジュールの両方の定義を含む "sample.exe" ファイルを 1 つ作成します。複数モジュールに対して異なるファイルを指定する場合は、各クラスタに対して


   module
      

を使用し、クラスタのすべてのクラスに対するオプションをオーバーライドできます。

   system
      sample
   root
ROOT_CLASS:
      "make"
   default
      il_generation
      ("exe") -- DLL を生成するには "dll"
      を使用します。
   cluster
      root_cluster:
      "c:\my_app"
         default
            module
      ("my_app")
         option
            module
      ("root"):
      ROOT_CLASS
         end
   ...

この ACE ファイルは 3 つのモジュールを定義しています。

  • 最初のモジュールは、アセンブリ記号を含む "sample.exe" です。

  • 2 番目のモジュール "my_app.dll" は、クラスタ root_cluster 内の、クラス ROOT_CLASS を除くすべてのクラスを含みます。

  • 3 番目のモジュール "root.dll" は、クラス ROOT_CLASS を含みます。このメカニズムによって必要なだけの数のモジュールを定義すること、および Eiffel システムの一部であるクラスを希望どおりにグループ化することも可能になります。

もうひとつの .NET Framework 固有の機能は名前空間の表記です。すべての .NET Framework の型は、システム内で型名の一意性を確保する名前空間に関連付けられます。以下の既定の ACE オプションを使用して、Eiffel システムのすべてのクラスに対して既定の名前空間を定義できます。

   system
      sample
   root
ROOT_CLASS:
      "make"
   default
      il_generation
      ("exe") -- DLL を生成するには "dll"
      を使用します。
      namespace
      ("MyApp")
   ... 

この例では、名前空間 "MyApp.<cluster_name>" に Eiffel システムのすべてのクラスが生成されます。ここで、<cluster_name> はクラスを含むクラスタの名前です。以下のように、クラスタごとの既定の名前空間をオーバーライドできます。

   system
      sample
   root
ROOT_CLASS:
      "make"
   default
      il_generation
      ("exe") -- DLL を生成するには "dll"
      を使用します。
      namespace
      ("MyApp")
   cluster
      root_cluster:
      "c:\my_app"
         default
            module
      ("my_app")
            namespace
      ("Root")
         option
            module
      ("root"):
      ROOT_CLASS
         end
   ...
      

この ACE ファイルを使用して、クラスタ root_cluster のすべてのクラスは名前空間 "Root" に生成されます。cluster 句で指定された名前は、default 句で定義された名前空間には追加されないことに注意してください。Eiffel# クラスは alias 句を含む場合があります。"alias" キーワードについては、「外部クラス」を参照してください。この場合、その句で指定された名前は、ACE ファイルで指定された名前空間をオーバーライドします。

.NET Framework の動的な性質に基づいた Eiffel のもう 1 つの大きな違いは、実行時の構成要素の動作方法です。従来の Eiffel 環境では、コントラクト違反は例外を発生し、アサーション チェックのレベルはコンパイル時に決定されていました。このアプローチは、コントラクトを設定した機能を呼び出す側が異なるモジュールに存在する可能性のある .NET Framework では十分ではありません。コントラクトを設定したコンポーネントのクライアントが、どのレベルのコントラクト チェックを設定する必要があるかを決定できます。このため、コントラクトで使用可能な操作を定義する標準的なインターフェイスがあります。このインターフェイスは、コントラクトを設定したコンポーネントによって実装される必要があります。IContract と呼ばれるインターフェイスが、コントラクト チェックのレベルを設定する機能を定義します。

  • 事前条件チェック: 最下位レベル、機能の事前条件のみがチェックされます。

  • 事後条件チェック: 中間レベル、事前条件および事後条件が共にチェックされます。

  • 不変条件のチェック: 完全チェック、すべてのコントラクト (事前条件、事後条件、および不変条件) がチェックされます。

IContract を使用して、コントラクト違反が発生したときに例外を発行するかどうか選択できます。

interface
      IContract
{
   //
      変更の対象
   public bool
      precondition_activated;
   public bool
      postcondition_activated;
   public bool
      invariant_activated;

   public void
      enable_precondition();
   public void
      enable_postcondition();
   public
      void
      enable_invariant();

   public
      void
      disable_precondition();
   public
      void
      disable_postcondition();
   public
      void
      disable_invariant();

   public
      bool
      exception_on_violation();
   public
      void
      enable_exception_on_violation();
   public
      void
      disable_exception_on_violation();

   public
      ContractException last_contract_exception();
}

Eiffel# コンパイラは、ACE ファイルで指定されている既定のコントラクト チェック レベルで自動的に IContract インターフェイスのインプリメンテーションを生成します。

   system
      sample
   root
ROOT_CLASS:
      "make"
   default
      il_generation
      ("exe")
      assertion (require)
      -- no、require、ensure、または
      
         invariant
      が使用できます。
   ...  

assertion オプションを省略すると、IContract は生成されません。オプションで 'no' を選択すると、IContract は生成されますが、クライアントがコントラクト チェックをアクティブにするまで、コントラクトのチェックは行われません。

.NET Framework の利用

これまで .NET Framework コンポーネントを構築するための Eiffel# の使用法について見てきました。コンパイラが必要なすべてのメタデータを生成するので、ほかの言語はあらゆる方法 (継承またはクライアント リレーションシップ) で Eiffel# のコンポーネントを再利用できます。次に問題になるのは、「Eiffel# で既存のコンポーネントを再利用する方法」です。既存のコンポーネントは、ほかの製造者が作成したものだけでなく、Microsoft のライブラリも含みます。

計画

ISE は Microsoft フレームワークをラップする Eiffel# ライブラリを提供します。これらのライブラリには、基本クラス ライブラリ、Win Forms、および Web Forms のラッパーを含みます。基本クラス ライブラリは、コレクション、リモート サービス、スレッド サービス、セキュリティ、および IO アクセスなどの、すべてのシステムで必要なベース型の定義を含みます。

Win Forms クラスを使用して、.NET Framework で GUI を構築できます。これらのクラスは、GUI を構築するためのクリーン オブジェクト モデルを提供する Win32 API のラッパーです。Web Forms は、Web 上に GUI を構築する方法を提供し、DataGrid または HTMLImage のような型を含みます。

これらの 3 つのライブラリは Eiffel# と共に出荷されるので、これらのクラスをシステムで再利用できます。

Emitter

アクセスする可能性がある .NET Framework コンポーネントは、当然 Microsoft ライブラリだけではありません。システムは、さまざまな言語で記述された何百ものコンポーネントの統合を要求する可能性があります。このため ISE は、すべての .NET Framework アセンブリを分析でき、定義するすべての型に対して Eiffel ラッパーを作成する Emitter と呼ばれるツールを提供します。Emitter はアセンブリで定義された各型にバインドされるメタデータにアクセスし、Eiffel の等価な項目にマップし、対応するクラスを生成します。

アセンブリは、"外部" アセンブリと呼ばれるほかのアセンブリの参照を含む場合がありますが、Emitter は指定されたアセンブリで定義されている型に対してクラスを生成するだけです。この結果、たとえば、ほとんどすべてのアセンブリは基本クラス ライブラリを参照するので、ラップする必要があるすべてのライブラリに対して基本クラス ライブラリを作成することを防ぎます。

すべてのパブリック .NET Framework 型は CLS (Common Language Specification) に準拠する必要があり、CLS はいくつかの面において Effel モデルとは異なります。そのため、Emitter は Eiffel クラスに .NET Framework 型をマップするために、単純ではない変換操作を実行する必要があります。おそらく、最も重要な違いは、CLS にオーバーロードを含むことです。Eiffel モデルはオーバーロードを禁止し、オーバーロードされた機能を明確にする必要があります。この目的で使用されるアルゴリズムは以下のとおりです。

f1, f2, ..., fn を同じ名前でオーバーロードされる .NET
      関数とします。
   (n >= 2)

1 <= i <= n の間、Si を
      fi のシグネチャとして設定します。

   Si = [Ti1, Ti2, ...,
      Tim]
      (im >=
      0)   

すべての Si はオーバーロードの規則によって異なります。
少なくとも u <=
      jm および Tju /= Tiu であるもう 1 つの
   関数 fj (1 <= j <=
      n, j /= i) が存在する場合は、関数 fi (for 1 <= u <= im)
      
      に対して u の位置は一意です。

次のように fi
      に対して一意名 Ni を決定します。
   [Ti1, Ti2, ... ,Tiu] である
      N_Ti1_Ti2..._Tiu (0 <= u <= im)
      
      は、ほかのすべての関数に対応する部分列とは異なる
         最小の最初の部分列です。オーバーロードの規則によって
            そのような部分列が存在し、Si
      を一意に評価します。 

簡潔に言うと、このアルゴリズムは一意名を取得するために、関数名の後に必要な数だけシグネチャ型の名前を追加します。たとえば、以下の C# の関数は、

   public static void
      WriteLine (String format, Object
      arg0);
   public static void
      WriteLine (int value);
   public
      static void WriteLine (String value);
      

Emmitter により、以下のように Eiffel に変換されます。

   WriteLine_System_String_System_Object
      (format: STRING; arg0:
      ANY)
   WriteLine_System_Int32
      (value:
      INTEGER)
   WriteLine_System_String
      (value: STRING) 

Emitter が、引数の型に対して行うベース型のマッピングに気がつくでしょう。以下の表は、CLS で定義される基本的な型のすべてのそれと同等な Eiffel の型を一覧しています。

CLS の基本的な型 (説明) Eiffel 型
System.Char (2 バイト符号なし整数) CHARACTER
System.Byte (1 バイト符号なし整数) INTEGER_8
System.Int16 (2 バイト符号付き整数) INTEGER_16
System.Int32 (4 バイト符号付き整数) INTEGER
System.Int64 (8 バイト符号付き整数) INTEGER_64
System.Single (4 バイト浮動小数点数) REAL
System.Double (8 バイト浮動小数点数) DOUBLE
System.String (0 文字以上の文字列。null は 許可されています。) STRING
System.Object (すべてのクラス継承階層のルート) ANY
System.Boolean (True または False) BOOLEAN

Emitter が有効な Eiffel コードを生成できない場合がいくつかあります。このような場合、生成されたクラスを手作業で編集する必要があります。さいわい、このような編集を行う必要があるのはごくまれなことです。600 種類以上あるすべての基本クラス ライブラリをラップする場合に、編集する必要があったクラスは 4 つだけでした。この間違った生成の原因となる問題点は、MethodImpls と呼ばれるメカニズムを使用することにあります。このメカニズムを使用すると、ある型に対してそのインターフェイス関数を異なる名前または異なるシグネチャを持つ関数にバインドすることができるようになります。Common Language Runtime で共分散が使用できる場合、これらの MethodImpls がよく使用されます。残念ながら、現段階では .NET Framework のリフレクション メカニズムで MethodImpls の情報にアクセスできません。

外部クラス

Emitter が生成する Eiffel# クラスはロジックを含みません。クラスは Eiffel 型システムに対してのみ必要になります。つまり、これらのクラスに対して Eiffel# コンパイラは IL を作成しません。Emitter によって生成されたクラスでの関数呼び出しは、.NET Framework 型に対する直接呼び出しになります。間接呼び出しがないため、パフォーマンスの問題は生じません。このようなクラスをコンパイラが認識するために、ISE は "外部クラス"と呼ばれる新しいメカニズムが Eiffel に導入されました。このようなクラスは Eiffel で記述されていない、既存の .NET Framework 型のメソッドまたは関数である外部機能のみを含むことができます。外部クラスのすべての機能は、同じ .Net Framework 型に属している機能である必要があります。このような型は、以下の構文で宣言できます。

frozen external
      class
   SYSTEM_CONSOLE
alias   
   "System.Console"
... 

  alias

に続く文字列は Eiffel クラスがラップする .NET Framework 型の名前を含んでいます。.NET Framework 型は "シールド" (この型からほかの型の継承を禁止します) である場合があります。Eiffel にはこのような概念がないので、Eiffel# では新しい Eiffel キーワード "frozen" を使用します。.NET Framework 型から Eiffel# クラスを継承できないことをEiffel# のコンパイラに知らせるために、

external 

の前に

frozen

を使用できます。
外部クラスではアクセスする必要のあるすべての機能をリストする必要があります。これらすべての機能は外部機能です。.NET Framework 外部機能の構文は以下の通りです。

frozen ReadLine: STRING
      is
   external
      "IL
      static signature :System.String use
      System.Console"
   alias
      "ReadLine"
   end 

ここで

  frozen

は、その機能が子孫で再定義されない可能性があることを示しています。外部機能が "仮想" である場合、その外部機能を再定義する必要がある場合があることに注意してください。その場合、

  frozen

は使用できません。ReadLine は、Eiffel# の機能の名前です。 STRING はこの機能の戻り値の型です。"external" キーワードに続く文字列は external の種類、.NET Framework 関数のシグネチャ、および関数が定義されている .NET Framework の型を指定します。"alias" キーワードに続く文字列はその関数の .NET Framework 名を含んでいます。アクセスを提供するメソッドの型に応じて、異なる種類の外部機能があります。Eiffel# は、次の表に一覧されている新しい 7 種類の外部機能を定義しています。

Microsoft .NET Framework 機能の種類 Eiffel の External
Method "IL signature ... use class_name"
Static Method "IL signature ... static use class_name"
Field Getter "IL signature ... field use class_name"
Static Field Getter "IL signature ... static_field use class_name"
Field Setter "IL signature ... set_field use class_name"
Static Field Setter "IL signature ... nbsp; set_static_field use class_name"
Constructor "IL signature ... creator use class_name"

必要であれば、このような外部機能を外部クラス以外のクラスで定義できます。特別な外部クラスの場合には、クラスの宣言の後に続く、"external" キーワードに続く文字列の最後に表れる .Net Framework 型名は、"alias" キーワードの後に表れる型名と同じである必要があります。「外部クラス」のコード フラグメントを参照してください。

外部機能は、ほかのすべての Eiffel 機能の呼び出しと同じ方法で、クライアントまたは子孫クラスから呼び出すことができます。そのため、システムにユーザー入力が必要な機能が含まれる場合、以下のコードを含めることができます。

   need_user_input
      is
         --
      ユーザー入力を取得および操作します。
      local
         io:
      SYSTEM_CONSOLE
         input:
      STRING
      do
         create
      io.make
         input
      := io.ReadLine --
      呼び出し
            System.Console.ReadLine()
         --
      操作します。
      end 

ReadLine は静的外部機能なので、呼び出すためにラッパーぼインスタンスを作成する必要はありません。そのため、外部機能の呼び出しは、以下のコードで十分です。

   need_user_input
      is
         --
      ユーザー入力を取得および操作します。
      local
         io:
      SYSTEM_CONSOLE
         input:
      STRING
      do
         --
      io
      の作成は削除されました。
         input
      := io.ReadLine --
      呼び出し
            System.Console.ReadLine()
         --
      操作します。
      end  

このコードは、静的フィールドのアクセスおよび静的フィールドの設定にも有効です。ほかの種類の外部機能は、ラッパーのインスタンス作成を要求します。

Eiffel# ライブラリ

Eiffel# コンパイラは、Eiffel# 基本クラス ライブラリと一体になっています。Eiffel# 基本クラス ライブラリは、標準 EiffelBase ライブラリの設計原則に従っています。このライブラリは、システムで再利用可能なクリーンで優れたデータ構造のセットを提供するために、汎用性と取り決めを広範に使用します。このライブラリは Eiffel# でも提供されている、.Net Framework 基本クラス ライブラリをラップする外部クラスを使用します。

コンパイラで提供されているほかの 2 つのクラスのセットは、Win Forms .Net Framework ライブラリと Web Forms .Net Framework ライブラリをラップしているので、GUI と Web アプリケーションを簡単に構築できます。

Microsoft .NET Framework コントラクト ウィザード

この開発の一部として、新しいツール Microsoft .NET Framework コントラクト ウィザードを作成しました。このウィザードは、メタデータ メカニズムを使用して、ユーザーが対話的に Eiffel 同様のコントラクトを任意の言語の .Net Framework コンポーネントに追加することを可能にしています。このツールについては、別の文書で詳しく説明する予定です。これは、Eiffel 以外の言語の契約による設計 (Design by Contract) 利点の適用を希望する開発者には既に入手可能になっています。

むすび

このプロジェクトの目標とその成果は、ISE Eiffel と Microsoft .Net Framework の完全な統合を提供することです。プラットフォームの能力と開発環境の組み合わせは、今日の社会が期待している優れたインターネット アプリケーションを構築する理想的な環境を作り出します。Microsoft .Net Framework 上の Eiffel は、柔軟性、生産性、および高い信頼性を提供します。特に、分散環境での契約による設計 (Design by Contract) を過大評価することはできません。分散環境で、開発後のバグを見つけだすことわずらわしく、費用がかかることを経験しています。Microsoft .Net Framework 上の Eiffel は、Eiffel メソッドのその他の利点と共に、意欲的なインターネット ソフトウェア開発者に最善のソリューションを提供します。Eiffel メソッドには、シームレスな開発、汎用プログラミング、情報隠ぺいとその他のソフトウェア エンジニアリングの原則、および優れた継承メカニズムなどがあります。

参考文献

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

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

ISE Eiffel の Web サイトは http://www.eiffel.com/ です。

--------------------------------------------------

Raphael Simon は、Interactive Software Engineering 社 (カリフォルニア州サンタバーバラ) の上級エンジニアで、Windows アプリケーションおよびツール部門の責任者です。Emmanuel Stapf は、ISE の上級エンジニアで、コンパイラおよび環境部門の責任者です。Christine Mingins は、メルボルン (オーストラリア) のモナッシュ大学の準教授兼副学長です。Bertrand Meyer は、ISE の CTO で、モナッシュ大学の教授です。著者と連絡と取りたい場合は、info@eiffel.com にアクセスしてください。