NullObjectパターン

http://d.hatena.ne.jp/akiramei/20071010/1192038349
↑はるか昔に似たようなことやった気がしていたのですが、はてなで日記を書く前でした。折角なので、数年間更新していない自分のHPからコードを持ってきてGeneric対応させてみました。当時(2003年)は、RealProxyがマイブームだった模様(笑)。

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;

class NullProxy<T> : RealProxy
{
    private static readonly Dictionary<Type, WeakReference> hash = 
        new Dictionary<Type, WeakReference> ();
    static readonly object syncObj = new object ();

    private NullProxy () : base (typeof(T)) { }

    /// <summary>
    /// メソッド呼び出しに割り込んで何もしないで抜ける。
    /// メソッドに戻り値がある場合、参照型ならnull、値型なら初期値を返す。
    /// </summary>
    /// <param name="msg">メッセージ通信オブジェクト</param>
    /// <returns>ReturnMessageオブジェクト</returns>
    public override IMessage Invoke (IMessage msg)
    {
        IMethodCallMessage req = msg as IMethodCallMessage;
        object o = null;

        Type t = *1
                o = Activator.CreateInstance (t);
        ReturnMessage rm = new ReturnMessage (o, null, 0, req.LogicalCallContext, req);
        return rm;
    }

    // 型毎にインスタンスは一つしか作らない
    public static T GetInstance ()
    {
        lock (syncObj)
        {
            NullProxy<T> result = null;
            Type t = typeof (T);
            WeakReference w = hash.ContainsKey(t) ? hash [t] : null;
            if (w != null)
                result = w.Target as NullProxy<T>;
            if (result == null)
            {
                result = new NullProxy<T> ();
                hash[t] = new WeakReference (result);
            }
            // 透過プロキシを返す
            return (T)result.GetTransparentProxy ();
        }
    }
}

/// <summary>
/// MarshalByRefObjectから継承する必要がある
/// </summary>
class Person : MarshalByRefObject
{
    private readonly string name = "Taro";
    private readonly int age = 20;

    public int Foo()
    {
        return 42;
    }

    public void Bar()
    {
        Console.WriteLine ("Bar");
    }

    public override string ToString ()
    {
        return String.Format ("Name={0}, Age={1}", name, age);
    }
}

class Program
{
    static void Main()
    {
        Person cp = NullProxy<Person>.GetInstance ();
        // 42が表示・・・されない
        Console.WriteLine (cp.Foo ());
        // Barが表示・・・されない
        cp.Bar ();
        // Name=Taro, Age=20が表示・・・されない
        Console.WriteLine (cp.ToString ());
    }
}

*1:MethodInfo)req.MethodBase).ReturnType; if (t.IsValueType) if (t != typeof (void