解説インサイド .NET Framework [改訂版]第1回 マネージ・コード/アセンブリ/モジュール吉松 史彰 2003/06/25 |
マルチ・モジュール・アセンブリの必要性
VS .NETですら生成できないマルチ・モジュール・アセンブリを、わざわざコマンドラインでコンパイラを実行してまで作る必要があるのか、という疑問がわくだろうか。マルチ・モジュール・アセンブリを必要とする理由は2つある。
第1の理由は、ワーキング・セットのサイズの縮小である。ワーキング・セットというのは、要するにアプリケーションが実行中に使うリソース(特にメモリ)量である。詳細は次回に解説するが、CLRは、必要になるまでモジュールをロードしないため、メモリが節約できるのだ。
第2の理由はクロス言語開発である。1つのモジュールを複数のプログラミング言語で開発することはできないが、別々のプログラミング言語で開発したモジュールをリンクしてアセンブリを作ることはできる。2人のプログラマーがいたときに、必ずしもプログラミング言語をそろえなくても、最終的に1つの成果物を作り出すことができるわけだ。各プログラミング言語のコンパイラが出力するものには、プログラミング言語固有の情報は入らない。そのため、アセンブリ・リンカやコンパイラは、どのプログラミング言語のコンパイラが出力したかに関係なく、モジュールをリンクできる。
マルチ・モジュール・アセンブリの欠点
もちろん、マルチ・モジュール・アセンブリにも欠点がある。分かりやすい問題としては、成果物の管理の複雑化が挙げられるだろう。複数のファイルが1つの論理的なまとまりを構成するので、シングルのときよりも管理が煩雑になる。それ以外にも、特に複数人開発で盲点となる欠点がある。それは、名前の衝突である。
例えば、プログラマーAさんが次のようなクラスを作ったとしよう。このプログラムのダメさ加減は置いといて(笑)。
|
|
プログラマーAさんがC#で作成したクラス |
これを次のコマンドでコンパイルして、モジュールを作成する。
% csc /t:module Util.cs
さて、シニア・プログラマーBさんは次のようなクラスを作った。
|
|
シニア・プログラマーBさんがVB.NETで作成したクラス |
これを次のコマンドでコンパイルして、やはりモジュールを作成した。
% vbc /t:module SuperUtil.vb
最後にプロダクト・マネージャのCさんがアセンブリを作成する。さて、次のコマンドを実行するとどうなるか。
% al /out:lib.dll Util.netmodule SuperUtil.netmodule
答えは「lib.dllが生成される」である。それでは次のようなコードで、出来上がったアセンブリを実行したら、何が起きるのだろうか。
|
|
2つのモジュールをリンクして作成したアセンブリを呼び出すコード |
アセンブリ・リンカは、クラス名の衝突を認識しない。実際には、Util.netmoduleにも、SuperUtil.netmoduleにも、MyCompany.Utilという名前のpublicなクラスが含まれているのだが、アセンブリ・リンカは、最初に見つけたクラスだけを公開し、2つ目のクラスは公開しない。どちらが公開されるかは、al.exeに渡す入力ファイルの順番に依存する。
このように問題もあるマルチ・モジュール・アセンブリだが、いろいろな場面でシングル・モジュール・アセンブリよりも威力を発揮する。その点についてはまた次回に取っておくことにしよう。
なぜモジュールとアセンブリを分けたのか
Windowsはこれまで、PE形式のファイルをロードすることでプログラムの実行を開始していた。あるプログラムが別のプログラムを呼び出すこともできたが、呼び出しの単位はファイルだった。つまり、1つ1つのファイルがそれぞれに独立して存在したのだ。
この仕組みは、次のようなコードに顕著に表れている。
|
|
LoadLibraryというAPIによりDLLをロードし、それに含まれるWin32 API(Beep)を呼び出すWindowsプログラム |
コードの中、LoadLibraryというAPIで、ファイル名を指定して呼び出している。このファイル名の文字列だけが、利用するライブラリへの手掛かりだ。
このような、物理的なファイル同士が緩やかに連携するスタイルには、メリットもあったが弊害もあった。特に大きな弊害が、いわゆる「DLL地獄(DLL hell)」と呼ばれる問題である。DLL地獄は、DLLとそれを利用するアプリケーションが、それぞれ独立して存在し、独立して配布され、独立してロードされることに問題の根源がある。例えば、1つのアプリケーション(.exe)と3つのDLLを1つの単位として構成できなかったため、DLLのうち1つだけを互換性のないバージョンにアップグレードしてしまうことが可能になっていたわけだ。しかも、DLLを呼び出している側のコードからは、いつ何がバージョンアップされたのか分からないし、自分がそもそもどのバージョンを使っているのかも分からない。
この問題は、根本的には、「コンポーネント」の概念が明確でなかったことに由来している*4。CDプレーヤとアンプとスピーカーがそろっていなければ音が鳴らないように、“A.exe”と“B.dll”と“C.jpg”がそろっていなければ、プログラムとして役に立たない場合があるだろう。しかしこれまでは、Windowsのファイル・ベースの仕組みのせいで、これら3つはあくまでも3つのファイルにすぎず、3つで1つの「コンポーネント」になることができなかった。
これに対し.NET Frameworkでは、コンポーネントの定義として、「アセンブリ」を用意した。Windowsを尊重して、アセンブリは物理的には複数のファイルから成り立っている。しかし、それら複数のファイルが論理的に1つのコンポーネントを構成することを明示するために、アセンブリとマニフェストが存在するわけだ。マニフェストに書いてある物理ファイル(モジュールとリソース)がすべてそろって、初めてプログラムが動作するようにする。それがアセンブリの目的である。そしてこれが.NET Frameworkでいうコンポーネント指向なのである。
*4 Component Object Model(COM)ですら、コンポーネントについて明確な定義をしていない。そのため、COMコンポーネントというのが、DLLを指すのか、クラスを指すのかあいまいなまま今日まできてしまっている。 |
今回のまとめ
今回は、.NET Frameworkの理解で最も重要な、アセンブリの概観を解説した。アセンブリが作成できるようになったので、次回はアセンブリがロードされて実行されるまでを解説していくことにしよう。
INDEX | ||
解説 インサイド .NET Framework [改訂版] | ||
第1回 マネージ・コード/アセンブリ/モジュール | ||
1.はじめに | ||
2.プログラムが提供する詳細情報 | ||
3.アセンブリとモジュールの作成 | ||
4.マルチ・モジュール・アセンブリの作成 | ||
5.マルチ・モジュール・アセンブリの必要性 | ||
「解説:インサイド .NET Framework [改訂版]」 |
- 第2回 簡潔なコーディングのために (2017/7/26)
ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている - 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう - 第1回 明瞭なコーディングのために (2017/7/19)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える - Presentation Translator (2017/7/18)
Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|