DIコンテナにおけるAOP用途プロキシの処理コストと型の違い

5/31のエントリ?に対して、すぎもとさんに言及頂いた。

インスタンスの生成が頻繁に必要で、インスタンス生成時のコストを下げたい場合は、S2Container.NET標準のAopProxyが有利です。

逆にインスタンスの生成時のコストがそれほど重要では無い場合は、Aspectを織り込んだメソッドの実行が早いSeasar.DynamicProxyが有利です。

このあたりをProjectの性質に合わせて、どちらを採用するか決定する必要がありますね。
[Seasar.DynamicProxyのパフォーマンス - sugimotokazuyaの日記]

DynamicProxyに関してですが、正確には「インスタンスの生成、取得」ではなく「型(クラス、インタフェース)の生成、取得」の処理コストが高いということです。

DynamicProxyは名前こそ"プロキシ"だが、実際にはAspectの織り込み(Weaving)を拡張された型により実現している。具体的には、型を生成すると共に、対象のメソッド全てをコールバックするコードを動的(デマンド)に生成しているために、通常の型に比べて、型の生成、取得に非常に時間がかかってしまうのだ。(複雑な、メソッドの多い型ほど処理に時間がかかる)
その代わり、一度型が生成されてしまえばあとは通常の型と同じように扱えるので、例えばActivaotr.CreateInstanceメソッドでインスタンスを生成する処理コストは普通の型となんら変わりないし、RealProxyのような、メソッドの実行そのものに付随するプロキシ特有のオーバヘッドは無い。
この、DynamicProxyによる型生成時の処理コストだが、削減できる可能性がある。
本家Seasar2(2.4)はJavaで書かれているが、Javaでは比較的重い処理である型情報を取得する処理を繰り返すのを嫌って、一度取得した型情報(型、メソッド、フィールド)を内部でキャッシュしている。(これが某ベンチマーク記事でSeasar2がSpringより速かった理由である)
.NETは型情報はメタデータとしてモジュールに埋め込まれるので、通常の型を扱う場合、キャッシュの効果は薄いが、DynamicProxyで動的に拡張される型のように、生成に処理コストがかかる型に関してはキャッシュの効果が高いと思われるのだ。(拙作のDIコンテナは先日のエントリ「DIにおけるリフレクション情報のキャッシュ (3」で書いている通り、型情報のキャッシュを実施している)

あと、DynamicProxyベースと、RealProxyベースでは拡張した型の代入互換性において違いがあるので注意が必要だ。下記のサンプルコードを見て頂きたい。

IMethodInterceptor interceptor = new TraceInterceptor();
IPointcut pointcut = new PointcutImpl(new string{".*"});
IAspect aspects = new AspectImpl(interceptor, pointcut);
DynamicAopProxy proxy1 = new DynamicAopProxy(typeof(Hoge), new IAspect { aspects }, null, new Hoge());
object obj1 = proxy1.Create();
DynamicAopProxy proxy2 = new DynamicAopProxy(typeof(Hoge), new IAspect[] { aspects }, null, new Hoge());
object obj2 = proxy2.Create();
Console.WriteLine("obj1 IsAssignableFrom obj2 = " + obj1.GetType().IsAssignableFrom(obj2.GetType()));
Console.WriteLine("obj2 IsAssignableFrom obj1 = " + obj2.GetType().IsAssignableFrom(obj1.GetType()));

このように、同じDynamicProxyのインスタンスであるproxy1とproxy2で、Hogeクラスを拡張した型からobj1とobj2を生成しているが、最後の2行の実行結果は

obj1 IsAssignableFrom obj2 = False
obj2 IsAssignableFrom obj1 = False

となる。これは同じ型を祖にしてDynamicProxyで拡張した2つの型に代入互換性はないことを意味する。(当たり前だがobj1,obj2とも、Hogeクラスへの代入互換性はある)同一条件でDynamicProxyを使用して拡張した型なのだから、互いに代入互換性があるような気がするが、Hogeを拡張した型だからといって、代入互換性があるとはいえないのである。
(ちなみに、DynamicAOPProxyのインスタンスを一つだけにして、同じインスタンスからobj1とobj2を生成すると、結果は両方ともTrueとなる)

一方、このコードのDynamicAopProxyを通常のRealProxyを利用したクラスであるAopProxy等に変更して実行してみると、今度は

obj1 IsAssignableFrom obj2 = True
obj2 IsAssignableFrom obj1 = True

と結果は真逆になる。この違いこそがDynamicProxyベースとRealProxyベースの根本的な違い(型の拡張が伴うか否か)であり、注意すべき点だろう。