かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

イケテルINotifyPropertyChangedの実装の改善

2010年になってから6日もたって初のBlog更新になります。新年あけましておめでとうございます。今年も、マイペースですが試したことや、思ったこと、後ほんの少しの日常を綴っていこうと思います。
今年もよろしくお願いします。

さて、今回の記事のネタは以下の記事が元になってます。
ネタ元記事

意外というか予想より、はるかに遅かったINotifyPropertyChangedのPropertyChangedイベントの発行処理ですが、コメントに

ななしさんのコメントから引用
--
senderはこれでも取得できるみたいです。
var sender = senderExpression.Value;
デバッグ実行で表示される値を眺めてて見つけました。

とのタレコミがありました。多謝です!!ConstantExpressionは、Valueプロパティで値がとれるということみたいです。

MSDNにも

表された式の値に等しい Object。

と説明にあるのでDelegateに変換してDynamicInvokeするまでもなかったみたいです。
ということで、改良版のPropertyChangedEventHandlerExtensionsは以下のようになります。

public static class PropertyChangedEventHandlerExtensions
{
    /// <summary>
    /// イベントを発行する
    /// </summary>
    /// <typeparam name="TResult">プロパティの型</typeparam>
    /// <param name="_this">イベントハンドラ</param>
    /// <param name="propertyName">プロパティ名を表すExpression。() => Nameのように指定する。</param>
    public static void Raise<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();

        // ×:式を評価してsender用のインスタンスを得る
        // var sender = Expression.Lambda(senderExpression).Compile().DynamicInvoke();
        // ○:定数なのでValueプロパティからsender用のインスタンスを得る
        var sender = senderExpression.Value;

        // 下準備が出来たので、イベント発行!!
        _this(sender, new PropertyChangedEventArgs(memberEx.Member.Name));
    }
}

これで、10000回実行して比較してみました。

実装方法 時間
通常 13ms
性能悪いイケテル方法 3593ms
今回のイケテル方法 92ms

結構普通の方法に迫ってきたかも。memberEx.Member.Nameでプロパティ名を取得してるところのオーバーヘッドがほとんどなんだろうなぁ。
ここは、どうしようもないのだろうか?う〜ん。