MessageHanbdlerAttribute への変更

ハンドラの検索部分を拡張し、MessageHandlerCacheAttribute に対応しなくてはなりません。

public static bool InvokeOne(Type t, object obj, ref Message msg)
{
    Debug.Assert(t.IsInstanceOfType(obj));

    MessageHandlerCacheAttribute cache = (MessageHandlerCacheAttribute)t.GetCustomAttributes(typeof(MessageHandlerCacheAttribute), false);
    if ((cache == null) || (cache.Length == 0))
    {
      // ...従来の処理...
    }
    else
    {
        // cache を用いて検索を高速化する
        cache[0].InitializeHandlers(t);
        foreach (MethodInfo method in cache[0][msg.Msg])
        {
            // メソッド呼び出し
            bool handled = (bool) method.Invoke(obj, new Object[1]{ msg });

            // Windows Message がハンドリングされたらメソッドの検索を終了する
            if (handled) return true;
        }
    }

    return false;   // not handled
}

といった感じになりました。最初の if 文では cache が null になることはないようなので Length プロパティのみチェックするだけで十分かもしれません。また、これを受けて MessageHandelrCacheAttribute のほうも、

public MethodInfo this[int msgId]
{
    get
    {
        System.Diagnostics.Debug.Assert(handlers != null, "InitializeHandler が呼び出されていません。");

        ArrayList list = (ArrayList)handlers[msgId];
        if (list != null)
            return (MethodInfo) list.ToArray(typeof(MethodInfo));
        else
            return new MethodInfo[] { };
    }
}

と、空の配列を返すようにします。null を返すと foreach もエラーになるのにも対処できています。.NET ではこういう場合は空の配列を返すのは一般的なのでしょう、interface の実装を自動挿入したときにも、そのようなコードが自動的に挿入されていました。
ArrayList を利用しているのは、System.Array を用いると要素の追加・削除が面倒だったためです。