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)

コメント





Counter: 37857 (from 2010/06/03), today: 1, yesterday: 0