Home>C# Tips>

RealProxyを使ったNullオブジェクトパターン

更新日:2003/08/16

デザインパターンの1つであるNull Objectパターンのサンプルです。JavaやC#で素直に実装したらクラス毎に用意することになりますがRealProxyを使うことによって、たった一つの実装で全てのクラスに対応します。実はこのネタ、@ITの会議室に出したヤツなのですが全然相手にされませんでした(笑)。折角なのでSingletonにしたり、WeakReferenceを使ったり、ちょっと機能アップしてます。


クラス構成

NullProxy
任意の型に対してNullObjectを生成するクラス。
MyClass
NullObjectパターンの対象になるクラス。
MyApp
NullProxyの使用例。

nullobject.cs

// ProxyによるNullオブジェクトパターン

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

class NullProxy : RealProxy {
  private static Hashtable hash = new Hashtable();

  private NullProxy(Type t) : base(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 = ((MethodInfo)req.MethodBase).ReturnType;
    if (t != typeof(void))
      o = Activator.CreateInstance(t);

    ReturnMessage rm = new ReturnMessage
      (o, null, 0, req.LogicalCallContext, req);
    return rm;
  }

  // 型毎にインスタンスは一つしか作らない
  public static object GetInstance(Type t) {
    lock (typeof(NullProxy)) {
      NullProxy result = null;
      // WeakReferenceを使ってGCをじゃましない
      WeakReference w = hash[t] as WeakReference;
      if (w != null) 
        result = w.Target as NullProxy;
      if (result == null) {
        result = new NullProxy(t);
        hash[t] = new WeakReference(result);
      }
      // 透過プロキシを返す
      return result.GetTransparentProxy();
    }
  }
}

// テストクラス
class MyClass : MarshalByRefObject {
  public void Sub(int x, ref int y, out int z) {
    y = x * 10;
    z = x * 100;
  }
  public int Function() {
    return 100;
  }
}

interface MyInterface {
  void Method();
}


class MyApp
{
  static void Main(string[] args)
  {
    MyInterface mi = NullProxy.GetInstance(typeof(MyInterface)) as MyInterface;
    mi.Method();

    Random r = new Random();
    for (int i = 0; i < 100; ++i) {
      int y = 10;
      int z = 20;
      // 初期値をNullObjectとする

      MyClass my = NullProxy.GetInstance(typeof(MyClass)) as MyClass;
      if (r.Next() % 2 == 0) {
        my = new MyClass();
      }
      // myの生成がされなくても、構わずメソッドを呼ぶ
      my.Sub(i, ref y, out z);
      Console.WriteLine("Sub: y = {0}, z = {1}", y, z);      
      Console.WriteLine("Function: " + my.Function().ToString());
    }
  }
}

Home>C# Tips>