Schemeプログラミング言語(R6RS標準)の実装では、次のようにモジュールをインポートできます。
(import (abc def xyz))
システムはファイルを検索しようとします$DIR/abc/def/xyz.sls
どこ $DIR
は、Schemeモジュールを保持するディレクトリです。 xyz.sls
はモジュールのソースコードであり、必要に応じてオンザフライでコンパイルされます。
Ruby、Python、Perlモジュールシステムは、この点で似ています。
一方、C#はもう少し複雑です。
まず、プロジェクトごとに参照する必要があるdllファイルがあります。それぞれを明示的に参照する必要があります。これは言うよりも複雑で、ディレクトリにdllファイルをドロップし、C#に名前でピックアップさせます。
次に、dllのファイル名と、dllによって提供される名前空間との間に1対1の名前の対応はありません。この柔軟性は高く評価できますが、手に負えなくなる可能性もあります。
これを具体化するには、私がこれを言うとき、それはいいですusing abc.def.xyz;
、C#はファイルを見つけようとしますabc/def/xyz.dll
、C#が検索することがわかっているディレクトリ(プロジェクトごとに構成可能)。
Ruby、Python、Perl、Schemeのモジュール処理方法の方がエレガントだと思います。新興言語はより単純な設計で行く傾向があるようです。
.NET/C#の世界では、このようにして、間接レベルがさらに高くなっているのはなぜですか?
Framework Design Guidelines セクション3.3の次の注釈。アセンブリとDllの名前は、名前空間とアセンブリが分離している理由についての洞察を提供します。
BRAD ABRAMS CLRの設計の早い段階で、プラットフォーム(名前空間)の開発者ビューを、プラットフォーム(アセンブリ)のパッケージ化および展開ビューから分離することにしました。この分離により、それぞれを独自の基準に基づいて個別に最適化できます。たとえば、機能的に関連するタイプ(たとえば、System.IOのすべてのI/O要素)をグループ化するために名前空間を自由に因数分解できますが、パフォーマンス(読み込み時間)、展開、サービス、またはバージョン管理の理由でアセンブリを因数分解できます。
柔軟性が追加され、ライブラリ(質問ではモジュールと呼ぶもの)をオンデマンドでロードできます。
1つの名前空間、複数のライブラリ:
利点の1つは、あるライブラリを別のライブラリに簡単に置き換えることができることです。名前空間があるとしましょうMyCompany.MyApplication.DAL
、およびライブラリDAL.MicrosoftSQL.dll
、すべてのSQLクエリおよびデータベースに固有のその他のものを含みます。アプリケーションにOracleとの互換性を持たせたい場合は、DAL.Oracle.dll
、同じ名前空間を維持します。これからは、Microsoft SQL Serverとの互換性を必要とするお客様向けの1つのライブラリと、Oracleを使用するお客様向けのもう1つのライブラリを備えたアプリケーションを提供できます。
このレベルで名前空間を変更すると、コードが重複するか、各データベースのソースコード内のすべてのusing
sを変更する必要が生じます。
1つのライブラリ、複数の名前空間:
1つのライブラリに複数の名前空間があることも、読みやすさの点で有利です。クラスで、名前空間を1つだけ使用する場合は、この名前空間のみをファイルの先頭に配置します。
大規模なライブラリのすべての名前空間を持つことは、ソースコードを読む人にとっても、ライター自身にとっても混乱を招き、Intellisenseは特定のコンテキストで示唆することが多すぎるためです。
ライブラリが小さい場合、ファイルごとに1つのライブラリがパフォーマンスに影響します。各ライブラリは、必要に応じてメモリにロードし、アプリケーションの実行時に仮想マシンで処理する必要があります。ロードするファイルが少ないほど、パフォーマンスがわずかに向上します。
「名前空間」と「モジュール」の両方の用語をオーバーロードすることを選択しているようです。定義に適合しない場合に「間接的」と見なされることは、驚くべきことではありません。
C#を含む名前空間をサポートするほとんどの言語では、名前空間はモジュールではありません。名前空間は名前をスコープする方法です。モジュールは、動作をスコープする方法です。
一般に、.Netランタイムはモジュールの概念(暗黙的に使用しているものとは少し異なる定義)をサポートしていますが、使用されることはめったにありません。 SharpDevelopでビルドされたプロジェクトで使用されるのを見ただけで、主に、異なる言語でビルドされたモジュールから単一のDLLをビルドできるようになりました。代わりに、動的にリンクされたライブラリを使用してライブラリをビルドします。
C#では、名前空間は、同じバイナリ内にある限り、「間接層」なしで解決されます。必要な間接処理は、コンパイラやリンカの責任であり、あまり考慮する必要はありません。複数の依存関係を持つプロジェクトの構築を開始したら、外部ライブラリを参照します。プロジェクトが外部ライブラリ(DLL)への参照を作成すると、コンパイラがそれを見つけます。
Schemeでは、外部ライブラリをロードする必要がある場合は、最初に(#%require (lib "mylib.ss"))
のようなことをするか、私が覚えているように、外部関数インターフェイスを直接使用する必要があります。外部バイナリを使用している場合、外部バイナリを解決するために同じ量の作業を行う必要があります。ほとんどの場合、使用頻度の高いライブラリを使用しているため、それを抽象化するSchemeベースのシムがありますが、サードパーティライブラリとの独自の統合を作成する必要がある場合は、基本的に「ロードする」ための作業が必要になります。 " 図書館。
Rubyでは、モジュール、名前空間、およびファイル名は、実際には、想定しているように接続されていません。 LOAD_PATHは少し複雑になり、モジュール宣言はどこにでも置くことができます。 Pythonは、Cのサードパーティライブラリがまだ(小さな)しわを追加していることを除いて、Schemeで見ていると思っている方法で行うことに近いでしょう。
さらに、Ruby、PythonおよびLISPのような動的に型付けされた言語には、通常、静的に型付けされた言語と同じ「契約」へのアプローチがありません。動的に型付けされた言語では、通常、「コードは特定のメソッドに応答するという紳士の同意」であり、クラスが同じ言語を話しているように見える場合はすべて問題ありません。静的に型付けされた言語には、コンパイル時にこれらのルールを適用する追加のメカニズムがあります。C#では、このようなコントラクトを使用すると、これらのインターフェースの順守について少なくとも適度に有用な保証を提供することにより、すべてが同じコントラクトに対してコンパイルされるため、プラグインと置換をある程度の共通性の保証とともにバンドルすることができます。Ruby or Scheme 、実行時に機能するテストを作成してこれらの合意を検証します。どちらのソリューションにもコストとメリットがありますが、名前空間、モジュール、およびライブラリの概念を融合しても効果がありません。
メソッドの呼び出しには二重ディスパッチが必要ないため、これらのコンパイル時間の保証から測定可能なパフォーマンス上の利点があります。 LISP、Ruby、JavaScript、またはその他の場所でこれらの利点を得るには、今でも、特殊なVMでクラスをジャストインタイムで静的にコンパイルするわずかにエキゾチックなメカニズムが必要です。
C#エコシステムがまだ比較的未熟なサポートを持っていることの1つは、これらのバイナリ依存関係の管理です。 Javaは、必要な依存関係がすべてあることを確認するために対処するために数年前からMavenを使用していましたが、C#には、前に適切な場所に戦略的にファイルを配置することを含むかなり基本的なMAKEのようなアプローチがあります時間。