C#/friendクラスの代替案 の履歴(No.2)
更新friend クラスとは†
密接に関連する2つのクラス、ClassA と ClassB とがあったとき、 他のクラスには公開しないが、お互いの間でのみ公開したいメソッドを 実装したくなる事が多々あります。
C++だとこのようなとき、お互いを friend クラスとして宣言することで、 外から見ると private なメソッドに、お互いの間でだけ自由にアクセスできる ようになります。
C# の問題†
ところが C# には friend はありません。
似たような機能として internal というのがあるのですが、friend に比べて公開される範囲が 広すぎるので、完全に代替とすることができず、結構あちこちで議論の的になっています。
おそらく一番の問題は、アセンブリを分けていない状況でコーディングしていると、 実際には必要とされない internal なメンバーがインテリセンスに現れてしまい、 誤って呼び出してしまったり、本当に呼び出したいメンバーを探すのが大変に なってしまったりすることだと思います。
インターフェースを用いた解決策†
こういう場合、インターフェースを使うと、friend よりも限定した形で、 特定のメソッドのみを、特定のクラスにのみ公開する事ができる事に気づきました。
当たり前の方法なのかもしれませんが、不思議と他で同様の記事を発見できなかったので、 一応ここにメモしておきます。
LANG:C# // ClassB からのみアクセス可能な private なメンバー // privateMemberForClassB を持つクラス public class ClassA: ClassB.IClassA { void ClassB.IClassA.privateMemberForClassB() { // このメンバーは private } } // ClassA の private メンバー // privateMemberForClassB にアクセスするクラス public class ClassB { // 同じアセンブリ内、あるいは ClassB および // その子孫クラスからのみ使えるインターフェース protected internal interface IClassA { void privateMemberForClassB(); } // 実際にアクセスするコードの例 public void AccessToPrivate(ClassA a) { // キャストしたときだけ操作できる ( (IClassA)a ).privateMemberForClassB(); } }
もちろん、同じアセンブリ内で、わざわざ a を B.IClassA にキャストするコードを 書けば該当コードにアクセスできてしまいますが、間違って呼び出してしまうとか、 インテリセンスを汚してしまうとか、そういった、本来解決したかった問題は この方法で回避できていると思います。
ClassA のプライベート関数を virtual にすることはできませんが、もともと インターフェース経由でのアクセスになりますので、子孫クラスで定義し直せば virtual と同様の効果が得られます。
非常に頻繁に呼び出される関数を、 このようにインターフェース経由の呼び出しにしてしまうと、 パフォーマンス的な問題が発生する可能性もありますが、 それ以外に関しては、コーディングの手間としても、 コードの読みやすさとしても、 ほとんど不都合の感じられない記述になっていると思います。
(2009/02/09)