いけてるINotifyPropertyChangedの実装は、結構遅かった - かずきのBlog@Hatena
正月から興味深いエントリですね。
遅くなるのは毎回のCompileと引数に渡してるExpression生成ですかね。Compileはキャッシュしてしまえば毎回生成する必要は無いですが、そもそも以下のようにすることでCompileそのものが不要に。
public static void Raise2<TResult>(this PropertyChangedEventHandler _this, Expression<Func<TResult>> propertyName) { // ハンドラに何も登録されていない場合は何もしない if (_this == null) return; // ラムダ式のBodyを取得する。MemberExpressionじゃなかったら駄目 var memberEx = propertyName.Body as MemberExpression; if (memberEx == null) throw new ArgumentException(); // () => NameのNameの部分の左側に暗黙的に存在しているオブジェクトを取得する式をゲット var senderExpression = memberEx.Expression as ConstantExpression; // ConstraintExpressionじゃないと駄目 if (senderExpression == null) throw new ArgumentException(); var sender = senderExpression.Value; // 下準備が出来たので、イベント発行!! _this(sender, new PropertyChangedEventArgs(memberEx.Member.Name)); }
こうすると、実行時間は...。
ノーマル:5ms
イケテル:3700ms
カイリョ:84ms
※平均値じゃないですが。
これを更に高速化しようとイロイロと試してみたんですが、どうやらExpression<Func<T>>の生成に時間を取られる模様。
Raiseを呼び出さずにローカル変数として
Expression<Func<string>> expr = () => Name;
と、しただけで実行時間はほぼ同じくらいかかるし。全く素敵じゃなくなるけど、↓こんな感じにしておくとちゃんと早い。
public class KairyoEmp : INotifyPropertyChanged { private string _name; private static Expression<Func<string>> _expr; public string Name { get { return _name; } set { _name = value; if (_expr==null) _expr = () => Name; PropertyChanged.Raise2(_expr); } } public event PropertyChangedEventHandler PropertyChanged; }
ノーマル:5ms
カイリョ:10ms
うむ。意味のないコードになってしまいました。年始からこれじゃ先が思いやられる...。