かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

いけてるINotifyPropertyChangedの実装は、結構遅かった

前に紹介した、コードの字面上いけてる感じのINotifyPropertyChangedの実装ですが、結構内部で色々やってるので遅いんだろうな〜となんとなく思ってました。

ということで、今日時間をはかってみました。実験したコードは以下のようなコードです。

/// 前に紹介したイケテル実装
public class IketeruEmp : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            PropertyChanged.Raise(() => Name); // 素敵っ               
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

// 普通の真面目な実装
public class NormalEmp : INotifyPropertyChanged
{
    #region INotifyPropertyChanged メンバ
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string name)
    {
        if (PropertyChanged == null) return;
        PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
    #endregion
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name"); // 文字列になってしまうのよ
        }
    }
}

Nameというプロパティを持つ単純なクラスです。2つあって、片方は前に紹介した方法で、もう片方は、普通の実装方法です。これを以下のようなMainメソッドを書いて速度を比較してみました。

class Program
{
    static void Main(string[] args)
    {
        {
            var emp = new NormalEmp();
            // とりあえず何もしないイベントを登録
            emp.PropertyChanged += (sender, e) => { };
            
            var watch = Stopwatch.StartNew();
            // 1万回名前を変更してみる
            foreach (var i in Enumerable.Range(0, 10000))
            {
                emp.Name = "田中" + i;
            }
            Console.WriteLine("ノーマル: {0}ms", watch.ElapsedMilliseconds);
        }

        {
            var emp = new IketeruEmp();
            // とりあえず何もしないイベントを登録
            emp.PropertyChanged += (sender, e) => { };

            var watch = Stopwatch.StartNew();
            // 1万回名前を変更してみる
            foreach (var i in Enumerable.Range(0, 10000))
            {
                emp.Name = "田中" + i;
            }
            Console.WriteLine("イケテル: {0}ms", watch.ElapsedMilliseconds);
        }

    }
}

1万回ループして、PropertyChangedイベントを発行させています。イベントハンドラは、とりあえず何もしないものを登録してあります。実行してみると・・・

ノーマル: 3ms
イケテル: 1915ms

ということで、ちょっと実用するには性能を改善しないといけないみたいです・・・。
残念っ!