アリスはルイスにプラダを知られたくない

#プログラマが思春期の娘に言われると傷つく一言 – Togetterまとめ
http://togetter.com/li/294232

を見ていて、「お父さんと同じ名前空間に居たくない!」と「パパのコードと一緒にビルドしないで!」が秀逸だったので C# で実現してみました。正確には名前空間じゃなくて Prada というクラス名を見せないようにします(まあ、クラス名をネストの深い名前空間に入れておけば、名前空間自体を探ることも難しいと思うのですが…これはあとで調べてみよう)。

単純に AliceRoom のアセンブリのクラスを他のアセンブリに公開したくない場合は、private なり internal を使えばよいのですが、そうなると Lolita に Prada を渡したく。Lewis(今回はお父さん役)には見せたくないけども、友達の Lolita とは共有したい場合は、InternalsVisibleTo 属性を使ってフレンド共有をします。こうすることで internal クラスが LolitaRoom から見えるという状態になります。

// Lolita クラスにのみ公開する
[assembly: InternalsVisibleTo("LolitaRoom")]

namespace AliceRoom
{
    public class Alice : Room.IPerson
    {
        public void Init()
        {
            var bag = new Prada();
            bag.Owner = "Alice";
            bag.Content = "my secret";
            this.box = bag;
        }
        public string GetBagContent()
        {
            var bag = box as AliceRoom.Prada;
            if (bag == null)
                return "NONE";
            else
                return bag.Content;
        }
    }
    /// <summary>
    /// Plada クラスは LolitaRoom にのみ公開
    /// </summary>
    internal class Prada
    {
        // Owner を設定できるのは Alice だけ
        public string Owner { get; internal protected set; }
        // コンテンツは Lolita も取得できる
        public string Content { get; set; }
    }
}

さて、もうひと息やって、Prada オブジェクトを Lolita に渡すことにします。
渡し方をどうするかというと、Alice.Post( Lolita ) みたいに渡したいですよね。実際に渡す Prada オブジェクトは、以下の Main から見えない(公開しているのは LolitaRoom アセンブリのみ)ので、何を誰に渡すのかが必要です。しかし「何を」渡すか自体は、Alice と Lolita しか知らないので、どうやって渡すかは試案のしどころ。

class Program
{
    static void Main(string[] args)
    {
        var alice = new AliceRoom.Alice();
        var lolita = new LolitaRoom.Lolita();
        var lewis = new LewisRoom.Lewis();
        // Pradaを作成
        alice.Init();

        // AliceRoom.Prada は見れない
        Console.WriteLine("alice room : {0}", 
            alice.GetBagContent());
        Console.WriteLine("lolita room: {0}", 
            lolita.GetBagContent());
        // Lolitaに渡す
        alice.Post(lolita);
        Console.WriteLine("alice room : {0}",
            alice.GetBagContent());
        Console.WriteLine("lolita room: {0}",
            lolita.GetBagContent());

        // Lwewis から AliceRoom.Prada は見れない
    }
}

仕方がないので、渡すためのインターフェースを作ります。ここでは、IPerson インターフェースを作って object 型で渡しているのですが、途中シリアライズ化する方法もあります。そういう意味では INotify を使ってもいいと思います。

namespace Room
{
    public abstract class IPerson
    {
        protected object box { get; set; }
        public void Post(IPerson dest) {
            dest.box = this.box;
            this.box = null;
        }
    }
}

あと、Prada の Owner 情報は Alice なので、Lolita が勝手に書き換えてはいけません。クラス情報は Lolita に公開するけど、Prada.Owner.set は Alice だけが使えるように「internal protected set」にしておきます。

namespace LolitaRoom
{
    public class Lolita : Room.IPerson
    {
        public void Post(Room.IPerson dest, object obj)
        {
            this.box = obj;
        }
        public string GetBagContent()
        {
            var bag = box as AliceRoom.Prada;
            if (bag == null)
                return "none";
            else
                return bag.Content;
        }
    }
}

そんな訳で「お父さんと同じ名前空間に居たくない!」を実現したのがこちら。

サンプルコードは github
https://github.com/moonmile/DontLookMyNamespace

カテゴリー: 開発, C# パーマリンク