C#という言語を使い始めて7年ぐらいは経つわけですが、Azukiの実装中に長いこと不満に思っていたコトが一つ解決しましたので備忘録。 Azukiは一応最初からプラットフォームを抽象化して作ってあります。そのため、現在はWinFormsフレームワーク用にSystem.Windows.Forms.Controlを継承したAzukiControlというクラスをAzukiの内部処理で直接使っていません。代わりに、IUserInterfaceというinterfaceを定義してこれをAzukiControlに実装させ、内部処理ではIUserInterfaceのみ使用することで内部処理をプラットフォームから分離しています。ここで、処理が複雑化するにつれて「内部処理の都合からIUserInterfaceに追加したいけれどAzukiのユーザ(=DLLの外側)には公開したくないメソッドやプロパティ」を作りたくなってきます。こうしたケースでは次のようにすれば良かったようです:
- DLL外部に公開したいメソッド等をpublic interface(※)で定義
- DLL外部に公開したくないメソッド等を(※)を実装したinternal interfaceで定義
- 実装クラスは両方のインタフェースを実装する
プログラムの話なので、次の例(意味不明な例ですが)をご参照ください。
// Hoge.dllのソース
public interface IHoge
{
void Piyo(); // DLL内部では使う
}
internal interface IHogeInternal : IHoge
{
void Fuga();
}
class HogeImplX : IHoge, IHogeInternal
{
void Piyo() {}
void Fuga() {}
}
class HogeImplY : IHoge, IHogeInternal
{
void Piyo() {}
void Fuga() {}
}
class Hoge
{
public static IHoge Create( bool xOrNot )
{
return (xOrNot) ? new HogeImplX()
: new HogeImplY();
}
}
// HogeHoge.exeのソース
public class HogeHoge
{
static void Main()
{
IHoge x = Hoge.Create( true );
IHoge y = Hoge.Create( false );
x.Piyo();
x.Fuga(); // Fugaは見えない。コンパイルエラー
y.Piyo();
y.Fuga(); // Fugaは見えない。コンパイルエラー
}
}
まとめると、「内部処理の都合から公開インターフェイスに追加したいが、DLLのユーザには公開したくないメンバー」が欲しくなったとき、それらを「公開インタフェースを実装するinternal interface」で定義し、内部処理では後者を常に使うようにすれば幸せになれそうです。さもなければメソッド等のコメントに「Internal use only」などと書いておくことになるでしょう…私がAzukiでそうしてきたように。 もう少し早く気付いていたら良かったなぁ…。