名前空間エイリアス

とあるCOMアプリケーションをラップするライブラリをC#で開発している。COMアプリケーションを扱うにはもちろんTlbimp.exeを使いRCWのアセンブリを生成し、それを参照するわけだが、このCOMアプリケーションには複数のバージョンが存在し(例えば2005、2006、2007という感じで)それぞれのAPIには多少差異がある。

なので、このラップするライブラリもCOMアプリケーションのバージョンに合わせて開発したいわけだが、RCWのアセンブリはバージョンが違っても名前空間、型は同じなわけで全バージョンのアセンブリを参照して開発する事はできない。かといって、バージョン毎にプロジェクトを作って開発するのは手間がかかりすぎる。

とりあえず思いついたのはRCWのアセンブリを生成する時にバージョン毎に名前空間を変える事。名前空間を変えてしまえば衝突はしなくなるので、あとはシンボルと条件付きディレクティブを使って、参照する名前空間を切り替える。

#define Hoge2005

#if Hoge2005
using Hoge_2005.Interop;
#elif Hoge2006
using Hoge_2006.Interop;
#endif

これでいいと思ったが、よく考えてみればこの名前空間はラップしたライブラリを参照する側からも見えてしまう*1。このライブラリを使う人間がこの名前空間を参照して開発して、その後別のバージョンに切り替えたら全部変更する羽目になってしまう。

同じ名前空間、型情報を持つ複数のアセンブリを参照して、衝突を起こさずに開発する方法は無いのか?と思ったらこんなのがあった。
名前空間エイリアス修飾子(::演算子)とは? グローバル名前空間とは?[2.0のみ、C#] - @IT

名前空間エイリアス修飾子(::演算子)はC# 2.0で新しく導入された演算子で、名前空間に対して定義されたエイリアス(別名)により、クラスやほかの名前空間などを修飾する場合に使用する。

これを使えば任意のアセンブリ名前空間に別名を付けられるらしい。

さっそく以下のように名前空間エイリアスを設定してみた。*2

アセンブリ エイリアス
Hoge_2005 hoge2005
Hoge_2006 hoge2006
Hoge_2007 hoge2007
Hoge_2008 hoge2008

そして、この名前空間エイリアスを有効にするには、ソースの一番最初で「extern alias 名前空間エイリアス」を宣言する必要がある。

extern alias hoge2005; extern alias hoge2006; extern alias hoge2007; extern alias hoge2008;

#define Hoge2005

#if Hoge2005
using hoge2005::Hoge.Interop;
#elif Hoge2006
using hoge2006::Hoge.Interop;
#elif Hoge2007
using hoge2007::Hoge.Interop;
#elif Hoge2008
using hoge2008::Hoge.Interop;
#endif

こうすることで、無事できた。
記述量は増えてしまったが、一度書いてしまえば変更する事もないのでたいした事はない。

しかし、2.0になって増えた仕様をいまだに全部理解できていなかった。もう3.0も出ているというのに、反省。

*1:それが完全に見えなくなるようにラップしてしまえばいいが、いかんせん量が多すぎる

*2:参照設定したアセンブリのプロパティから設定できる