重いRealProxy#Invokeを他の良い方法で置き換える
先日のエントリの代替案(というかこちらの方がより良い)について、NyaRuRuさんに非常に詳しく解説頂いた。
パフォーマンスのための Delegate, LCG, LINQ, DLR (を後で書く) - NyaRuRuの日記
すぐにでも試したくてうずうずしているのだが今日は時間が無い。取り急ぎ御礼まで。
より良い方法を試す以前に直近の対策をどうしたかを書いておく必要があるだろう。
結果としては先日書いたとおり、1.のデータバインドのサスペンド中にはINotifyPropertyChanged#NotifyPropertyChangedをコールしないようにしただけである。
具体的には元々提示していたインタフェースに内部で使用しているBindingSourceを取得するメソッドかプロパティを追加しておき、AOPのインターセプタでセッタメソッドのフック時に現在、データバインドがサスペンドしているかを判定する。
public interface IEntity : INotifyPropertyChanged
{
void NotifyPropertyChanged(String propertyName);
BindingSource GetBindingSource();
}
〜
public class NotifyPropertyChangedInterceptor : AbstractInterceptor {
public override object Invoke(IMethodInvocation invocation) {
MethodBase method = invocation.Method;
if (method.Name.StartsWith("set_")) { //プロパティのセッターだけを対象に
IEntity entity = invocation.Target as IEntity;
if (entity != null && !entity.GetBindingSource().IsBindingSuspended)
{
string propName = method.Name.Split(new string[] { "set_" }, StringSplitOptions.None)[1];
PropertyInfo info = invocation.Target.GetType().GetProperty(propName,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
if (info != null) {
object preValue = info.GetValue(invocation.Target, null);
Object ret = invocation.Proceed();
object postValue = info.GetValue(invocation.Target, null);
if (preValue == null) {
if ( postValue != null ) {
entity.NotifyPropertyChanged(propName);
}
} else {
if (! preValue.Equals(postValue)) {
entity.NotifyPropertyChanged(propName);
}
}
return ret;
}
}
}
return invocation.Proceed();
}
}
単純で泥縄だが重いメソッドを呼び出すのを抑制することの効果は絶大で、これだけでも処理時間の50%以上を減らすことが出来る。