C#/friendクラスの代替案 のバックアップ差分(No.2)

更新


  • 追加された行はこの色です。
  • 削除された行はこの色です。
[[公開メモ]]

#contents

* friend クラスとは [#t75f84fc]

密接に関連する2つのクラス、ClassA と ClassB とがあったとき、
他のクラスには公開しないが、お互いの間でのみ公開したいメソッドを
実装したくなる事が多々あります。

C++だとこのようなときに、お互いを friend クラスとして宣言することで、
外から見ると private なメソッドに自由にアクセスすることができるようになります。
C++だとこのようなとき、お互いを friend クラスとして宣言することで、
外から見ると private なメソッドに、お互いの間でだけ自由にアクセスできる
ようになります。

* C# では [#med36db1]
* C# の問題 [#med36db1]

C# では friend はありません。
ところが C# には friend はありません。

代わりに internal というのがあるのですが、friend に比べて公開される範囲が広すぎて
完全に代替とすることができないところです。
似たような機能として internal というのがあるのですが、friend に比べて公開される範囲が
広すぎるので、完全に代替とすることができず、結構あちこちで議論の的になっています。

こういう場合、インターフェースを使うと、friend に似た効果を得られる事に気づきました。
-[[フレンドクラスに対する代替案>http://www.google.co.jp/url?sa=U&start=1&q=http://bbs.wankuma.com/index.cgi%3Fmode%3Dal2%26namber%3D7138%26KLOG%3D7&ei=UJ-PScykFoiU6gPu0um1Cg&usg=AFQjCNEPQcXCNVnqZhsEq526MW3doVSF2Q]]
-[[C#への期待。アンダースからの返答 − @IT>http://www.google.co.jp/url?sa=U&start=4&q=http://www.atmarkit.co.jp/fdotnet/insiderseye/20060215cscommunity/cscommunity_02.html&ei=UJ-PScykFoiU6gPu0um1Cg&usg=AFQjCNHK3iwgAkMt0olEa3_MwaYBjo24fw]]
-[[C# には friend キーワードがない>http://frog.raindrop.jp/knowledge/archives/002237.html]]
-[[Google で "c#" friend を検索>http://www.google.co.jp/search?q=%22c%23%22+friend&lr=lang_ja&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:ja:official]]

当たり前の事なのかもしれませんが、不思議と他で同様の記事を発見できなかったので、
一応ここにメモしておきます。
おそらく一番の問題は、アセンブリを分けていない状況でコーディングしていると、
実際には必要とされない internal なメンバーがインテリセンスに現れてしまい、
誤って呼び出してしまったり、本当に呼び出したいメンバーを探すのが大変に
なってしまったりすることだと思います。

    public class ClassA: B.IClassA
    {
        void B.IClassA.privateMemberForClassB()
        {
            // このメンバーは private
        }
    }
* インターフェースを用いた解決策 [#t2ec0098]

    public class B
    {
        internal interface IClassA
        {
            void privateMemberForClassB();
        }
こういう場合、インターフェースを使うと、friend よりも限定した形で、
特定のメソッドのみを、特定のクラスにのみ公開する事ができる事に気づきました。

        public AccessToPrivate(ClassA a)
        {
            // キャストしたときだけ操作できる
            ( (IClassA)a ).privateMemberForClassB();
        }
    }
当たり前の方法なのかもしれませんが、不思議と他で同様の記事を発見できなかったので、
一応ここにメモしておきます。

 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)

* コメント [#p09af981]

#article_kcaptcha


Counter: 46400 (from 2010/06/03), today: 1, yesterday: 5