IContextAttribute の実装
ContextAttribute クラスは IContextAttribute だけでなく、IContextProperty の面倒もみてくれていますが、おそらく、一般的な実装を提供してくれているので、そのまま ContextAttribute を利用してみます。
ContextAttribute クラスのコンストラクタは名前を要求してくるので、"MethodLogger" を指定して呼び出してみましょう。
[AttributeUsage(AttributeTarget.Class)] public class MethodLoggerAttribute : ContextAttribute { public MethodLoggerAttribute() : base("MethodLogger") {} } [MethodLogger] public class Test : ContextBoundObject { public Test() {} public static Test CreateNew() { return new Test(); } }
記述していませんが、ContextAttribute の仮想メソッドはすべて override して、呼び出しと結果を記録するだけの実装を追加しています。*1
この MethodLoggerAttribute を付与したオブジェクトを生成することで、ContextAttribute の役割が大体わかってきます。
まず、IContextAttribute.IsContextOK() が呼び出され、新規オブジェクトと現在のコンテキストが合致しているかどうかをチェックされます。このチェックに成功すると、以降の動作はすべてパスされます。
失敗した場合は、まず IContextAttribute.GetPropertiesForNewContext() が呼び出され、続けて、IContextProperty.Freeze() が呼び出された後、IContextProperty.IsNewContextOK() が呼び出されます。
動きだけでは予測し辛い面も多いため SDK 付属の IL Disassembler も活用し、ContextAttribute クラスの実装を簡単にチェックしてみると、GetPropertiesForNewContext() では、Context にプロパティを追加登録しており、IsContextOK() はこのプロパティがすでに登録されているかどうかをチェックしているようです。
IContextProperty の実装
IContextProperty 関係のメソッド呼び出しは、コールスタックを少し遡って見ると、ActivationServices.DoCrossContextActivation() などにたどりつくことができますが、用途も機能もわかりそうでわからないイメージですし、ContextAttribute クラスの実装がほとんど空なので、当面は深く考えないことにします。
AttributeUsage.AllowMultiple
MethodLoggerAttribute と SynchronizationAttribute を同時に設定すると、双方が共に初期化されますが、MethodLoggerAttribute を2つ設定しても、片一方にしかメソッド呼び出しが発生しません。
これは、GetPropertiesForNewContext() で設定するプロパティによらないようなので、引数によって複数の挙動を切り替えられるような属性を、機能として2つ以上コンテキストに設定することはできないということに注意しなければならないでしょう。
そのような用途には、属性を複数個設定するのえではなく、設定するプロパティの内容をみて挙動を切り替えられるようにすればよい、ということでしょうか。
*1:ContextAttribute は IContextAttribute と IContextProperty のすべてのメソッドを仮想メソッドとして実装している