かずきのBlog@hatena

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

いけてる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

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