實現INotifyPropertyChanged-是否存在更好的方法?

Microsoft應該爲INotifyPropertyChanged實現了一些簡單的功能,例如在自動屬性中,只需指定{get; set; notify;} {get; set; notify;} {get; set; notify;}我認爲這樣作頗有意義。 仍是有任何併發​​症要作? git

咱們本身能夠在屬性中實現「通知」之類的功能嗎? 是否存在用於在您的類中實現INotifyPropertyChanged的優雅解決方案,或者惟一的方法是經過在每一個屬性中引起PropertyChanged事件。 express

若是不能,咱們能夠寫一些東西來自動生成引起PropertyChanged事件的代碼嗎? 併發


#1樓

從.Net 4.5開始,終於有了一種簡單的方法。

.Net 4.5引入了新的呼叫者信息屬性。 wordpress

private void OnPropertyChanged<T>([CallerMemberName]string caller = null) {
     // make sure only to call this if the value actually changes

     var handler = PropertyChanged;
     if (handler != null) {
        handler(this, new PropertyChangedEventArgs(caller));
     }
}

最好在函數中添加一個比較器。 函數

EqualityComparer<T>.Default.Equals

這裏這裏有更多示例 post

另請參閱呼叫者信息(C#和Visual Basic) this


#2樓

我實際上尚未機會本身嘗試一下,可是下一次我要創建一個對INotifyPropertyChanged有很高要求的項目時,我打算編寫一個Postsharp屬性,該屬性將在編譯時注入代碼。 就像是: google

[NotifiesChange]
public string FirstName { get; set; }

會變成: spa

private string _firstName;

public string FirstName
{
   get { return _firstname; }
   set
   {
      if (_firstname != value)
      {
          _firstname = value;
          OnPropertyChanged("FirstName")
      }
   }
}

我不肯定這在實踐中是否可行,我須要坐下來嘗試一下,但我不知道爲何不這樣作。 對於須要觸發多個OnPropertyChanged的狀況,我可能須要使其接受某些參數(例如,若是我在上面的類中具備FullName屬性) code

目前,我在Resharper中使用自定義模板,可是即便那樣,我也厭倦了我全部的屬性太長了。


嗯,快速的Google搜索(我在寫這篇文章以前就應該這樣作)代表,至少有一我的在這裏以前作了相似的事情。 不徹底是個人初衷,但足夠接近以證實該理論是好的。


#3樓

在不使用postsharp之類的狀況下,我使用的最低版本使用的是:

public class Data : INotifyPropertyChanged
{
    // boiler-plate
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    // props
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }
}

那麼每一個屬性就像:

private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }

這不是很大; 若是須要,它也能夠用做基類。 SetFieldbool返回SetField告訴您是否爲空操做,以防您要應用其餘邏輯。


甚至更容易使用C#5:

protected bool SetField<T>(ref T field, T value,
    [CallerMemberName] string propertyName = null)
{...}

能夠這樣稱呼:

set { SetField(ref name, value); }

編譯器將使用該"Name"自動添加"Name"


C#6.0使實現更容易:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

...如今使用C#7:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
   => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));


private string name;
public string Name
{
    get => name;
    set => SetField(ref name, value);
}

#4樓

我真的很喜歡Marc的解決方案,可是我認爲能夠略做改進,以免使用「魔術字符串」(不支持重構)。 與其使用屬性名稱做爲字符串,不如使其成爲lambda表達式:

private string name;
public string Name
{
    get { return name; }
    set { SetField(ref name, value, () => Name); }
}

只需在Marc的代碼中添加如下方法,便可達到目的:

protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
    if (selectorExpression == null)
        throw new ArgumentNullException("selectorExpression");
    MemberExpression body = selectorExpression.Body as MemberExpression;
    if (body == null)
        throw new ArgumentException("The body must be a member expression");
    OnPropertyChanged(body.Member.Name);
}

protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(selectorExpression);
    return true;
}

順便說一句,這是受此 博客文章 更新URL的啓發


#5樓

在這裏查看: http : //dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx

它是用德語編寫的,可是您能夠下載ViewModelBase.cs。 cs文件中的全部註釋均以英語編寫。

使用此ViewModelBase-Class能夠實現相似於衆所周知的Dependency Properties的可綁定屬性:

public string SomeProperty
{
    get { return GetValue( () => SomeProperty ); }
    set { SetValue( () => SomeProperty, value ); }
}
相關文章
相關標籤/搜索