ViewModelのプロパティをちょっとだけ簡潔に書く方法

Mix11に使われていたと思われるコードが落ちていのたでつらつらと眺めていたら、ちょっと変わった方法でViewModelを定義していましたのでご紹介します。

普通のViewModelの場合

普通にViewModelを定義する場合こんな感じになるのではないでしょうか。PropertyChangedに対応したプロパティが増えるとViewModelの見通しが悪くなります。

public class BasicViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

public class BasicViewModel : BasicViewModelBase
{
    private string text = string.Empty;

    /// <summary>
    /// プロパティの定義に行数を使いすぎて見通しが悪くなる。
    /// </summary>
    public string Text
    {
        get
        {
            return this.text;
        }
        set
        {
            if (!Equals(this.text, value))
            {
                this.text = value;
                this.RaisePropertyChanged("Text");
            }
        }
    }
}

Mix11のサンプルにあったViewModelの場合

それに対して、refをうまく使ってSetValueメソッドを書くことによって、ViewModelに実際に書くプロパティの行数を減らしています。GetValueはSetValueと同じようにgetterを書くために用意したものと思います。

public class MixViewModelBase : INotifyPropertyChanged
{
    protected void SendPropertyChanged(string propertyName)
    {
        var e = new PropertyChangedEventArgs(propertyName);
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected T GetValue<T>(T field)
    {
        return field;
    }

    protected void SetValue<T>(string propertyName, ref T field, T newValue)
    {
        if (!object.ReferenceEquals(field, newValue) && (newValue == null || !newValue.Equals(field)))
        {
            field = newValue;
            this.SendPropertyChanged(propertyName);
        }
    }
}

public class MixViewModel : MixViewModelBase
{
    private string text = string.Empty;

    /// <summary>
    /// バインディングソース用プロパティの書き方がちょっとだけ簡潔になるけど
    /// ref使っているのがちょっと・・・
    /// </summary>
    public string Text
    {
        get { return this.GetValue(this.text); }
        set { this.SetValue("Text", ref this.text, value); }
    }
}

個人的にはrefを使うのがちょっとキモイ気もしますが、簡潔に書く一つの方法としてはありかもしれません。