コンテキスト (1)
というわけで、MethodLogger についてはおしまいなのだが、MethodLogger 作成後、ここに書き直してるうちにもいろいろ調べたことはあるので、それらについてちょっとづつ。
ProxyAttribute.CreateInstance() から得た proxy
これは、実質的に対象オブジェクトの this であるように振舞う。
InitializeServerObject() を target stub に対して実行すれば、目的の this はきっちり proxy で固定される。
IConstructionCallMessage ctor = msg as IConstructionCallMessage; if (ctor != null) { RemotingServices.GetRealProxy(this.target).InitServerObject(ctor); }
この InitServerObject で得られる ReturnValue は実インスタンスとなっていて、このメッセージを Invoke() の戻り値とすると、実インスタンスに対する参照が外に漏れてしまうので ContenxtBoundObject が例外を発するようになる。
this.InitServerObject(ctor) を呼び出した場合との違いとして、作成された ContextBoundObject にたいして GetRealProxy(this) が何を返すかであり、上記の例では System.Runtime.Remoting.Proxies.RemotingProxy が戻るようになる。
理由はわからないが、new の戻り値を MethodLogger の proxy にした場合、this を RemotingProxy の proxy にすると、new で得た参照に関する Invoke() が MethodLogger の Invoke() 呼び出しとして機能するようになる。(ただし、当然だが this に対する呼び出しは機能しない)
IMessageSink インターフェス
高度なリモート処理に関する文章で、もう1つ MethodLogger の実装として利用できそうなものとして、IMessageSink インターフェスを扱うことも考えられます。
ところが、このインターフェス。実装するのは難しくないのですが、登録するにはコンテキストについてもうちょっと理解が必要そうです。
スレッドコンテキストとか、デバイスコンテキストとか、コンテキストと呼ばれるものは山のようにあるわけだけど、ContextBoundObject でいうコンテキストとは、どのようなものなのだろうか?
ContextBoundObject とコンテキスト
困ったときは SDK ドキュメントを全文検索、というわけではないが全文検索。
クラスリファレンスと比べると、かなりましな説明を発見することができた。
コンテキストは、その内部にあるオブジェクトに対して実行時に高度な環境を提供する、アプリケーション ドメインの下位概念と考えることができます。
たとえば、コンテキストは、オブジェクトが同時に複数のスレッドからアクセスされないことを保証できます。
すべてのアプリケーション ドメインが、既定のコンテキストを持っています。
ほとんどのマネージ コードは、同じアプリケーション ドメイン内でそのドメインの既定のコンテキストを使用してオブジェクトを生成したりメンバを直接呼び出したりするため、コンテキストに関連する問題が発生することはありません。
ContextBoundObject から継承されたすべての型は、同じドメイン内または他のドメイン内にある他のコンテキストに対して、プロキシとして公開されます。
この文章を発見する前からわかっていたことだが、コンテキスト関係の概念を学ぶ上で、RealProxy や TransparentProxy に関する知識は基本的に必要ない。
実装としてそれらを利用していることと、リモート処理の基礎知識として要求されてしまう部分はいくつかあるが、たんなる用語であると思っていても問題ない程度だろう。
他にも System.Runtime.Remoting.Contexts 名前空間の序文なんかも読んでおくと理解の助けになるかもしれない。
ContextBoundObject の言うコンテキストとは、オブジェクト間のメッセージ交換において、オブジェクトをグループ分けするようなものだということが、大体予測付くのではないだろうか。
System.Runtime.Remoting.Contexts 名前空間
SDK ドキュメントでは、
TYPE 型は、.NET Framework インフラストラクチャのサポートを目的としています。独自に作成したコード内で直接使用することはできません。
という文章で何も情報がないが、Google などで検索すると、コンテキスト関係を利用する場合には、このあたりのクラス群を扱うことが、どうしても必要になるようだ。
public なクラスは、
- Context
- ContextAttribute
- ContextProperty
- SynchronizationAttribute
このうち SynchronizationAttribute に関しては、唯一 SDK によって解説されたクラスである。
public なインターフェスは、
- IContextAttribute
- IContextProperty
- IContextPropertyActivator
- IContributeClientContextSink
- IContributeDynamicSink
- IContributeEnvoySink
- IContributeObjectSink
- IContributeServerContextSink
- IDynamicMessageSink
- IDynamicProperty
最後に、public delegate として
- CrossContextDelegate
といったところで、これらについて簡単に見ていこうかと思う。
SynchronizationAttribute クラス
最初に見るのは、きちんとドキュメントされている SynchronizationAttribute から。
クラス名でわかるように属性であり、その機能もかなりシンプルなものになっている。
前述の全文検索の結果から、この属性を付与することによって得られる効果は説明文から十分に予測できるが、実際に試してみるほうがよくわかる。
[Synchronization(false)] public class Test1 : ContextBoundObject { public void test(object info) { Trace.WriteLine("+ " + info); try { Thread.Sleep(1000); } finally { Trace.WriteLine("- " + info); } } } public class SyncTest { static void Main() { Test1 t = new Test1(); for (int n = 0; n < 10; n++) { ThreadPool.QueueUserWorkItem(new WaitCallback(t.test), n); } Console.ReadLine(); } }
最初の SynchronizationAttribute を付けはずししてみることで、その効果を確認することができるだろう。
この時点で、属性によってメソッドの挙動を変化させているということに驚くべきなのだが、あまりにも簡単に挙動を変化させられてしまうので、ちょっと簡単に驚けない。
ContextAttribute クラスと IContextAttribute インターフェス
次の切り口として扱いやすいのは、この SynchronizationAttribute の親である ContextAttribute クラスと、それに非常に似た名前の IContextAttribute インターフェスではないかと目星をつけてみた。
実際、ContextAttribute は IContextAttribute の標準的な実装を提供するヘルパークラスであるようなので、当面の目的の1つとして IContextAttribute を実装した属性の作成があげられるだろう。
ContextProperty クラスと IContextProperty インターフェス
もう少し注意深く ContextAttribute クラスを見ると、IContextProperty インターフェスの実装もかねていることがわかる。
ContextProperty と IContextProperty は、ContextAttribute と IContextAttribute のような関係ではないことも、public インターフェスを比べてみることでわかる。
ContextProperty クラスは string と object のペアを保持するだけの何の機能も持たないデータクラスであることもすぐにわかるが、IContextProperty インターフェスのメソッドがどのような機能をもつかは、簡単にわかるようなものではない。
IContributeClientContextSink と IContributeServerContextSink
SynchronizationAttribute は、ドキュメントにはない上記2つのインターフェスを実装している。
この2つのインターフェスは名前が異なるだけで、どちらも前述の IMessageSink を取り出すための実装を提供するためのメソッドを要求する。
このインターフェスは、これまでの内容に対して、求めていたものそのものに近いことが予測される。
IContributeDynamicSink, IContributeEnvoySink, IContributeObjectSink
これら、似た名前のインターフェス群も、同様に名前の違う...つまり役割が異なる IMessageSink を取得することを可能とするためのインターフェスであることが予測される。
IDynamicMessageSink
IContributeDynamicSink から返される。
IMessageSink とは異なるインターフェスを持つメッセージシンクだとうと予測されるが、とりあえず IDynamicProperty と共に、用途が見えてくるまでは考えないでおこう。
Context クラス
おそらくコンテキストそのもの。
非常に豊富なメソッドを持っているので、一通りメソッド名を眺めておく程度にとどめておこう。
IContextPropertyActivator インターフェス
いくつか興味ある名前のメソッドを定義しているので、多少気に留めておこうかという程度。
ContextProperty クラスが名前と値のペアであることを考えると、IContextProperty の メソッドと対応付けて考えるべきだろう。
一通りのインターフェスを見たところで、大体狙いどころとなる目星はついたので、そこらへんから切り崩していけるのではないだろうか。