微软应该为INotifyPropertyChanged实现一些时髦的东西,就像在自动属性中,只需要指定{get;设置;通知;} 我认为这样做很有意义。或者做这个手术有什么并发症吗?

我们能在属性中实现类似notify的东西吗。在你的类中实现INotifyPropertyChanged是否有一个优雅的解决方案,或者唯一的方法是在每个属性中引发PropertyChanged事件。

如果不是,我们可以写一些东西来自动生成一段代码来引发PropertyChanged事件?


当前回答

这就是大规模的过度工程。这比仅仅以正确的方式进行要复杂得多,而且几乎没有任何好处。如果你的IDE支持代码片段(Visual Studio/MonoDevelop支持),那么你可以让这个实现变得非常简单。你需要输入的只是属性的类型和属性名。额外的三行代码将自动生成。

其他回答

=>这里我的解决方案具有以下特征

 public ResourceStatus Status
 {
     get { return _status; }
     set
     {
         _status = value;
         Notify(Npcea.Status,Npcea.Comments);
     }
 }

没有refelction 短的符号 业务代码中没有神奇的字符串 PropertyChangedEventArgs在应用程序中的可重用性 在一条语句中通知多个属性的可能性

看这里:http://dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx

它是用德语写的,但你可以下载ViewModelBase.cs。cs-File中的所有注释都是用英语写的。

使用这个ViewModelBase-Class,可以实现类似于众所周知的依赖属性的可绑定属性:

public string SomeProperty
{
    get { return GetValue( () => SomeProperty ); }
    set { SetValue( () => SomeProperty, value ); }
}

根据Thomas的回答,这是从Marc的回答改编而来的,我把反射属性更改代码变成了一个基类:

public abstract class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) 
            handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        var me = selectorExpression.Body as MemberExpression;

        // Nullable properties can be nested inside of a convert function
        if (me == null)
        {
            var ue = selectorExpression.Body as UnaryExpression;
            if (ue != null)
                me = ue.Operand as MemberExpression;
        }

        if (me == null)
            throw new ArgumentException("The body must be a member expression");

        OnPropertyChanged(me.Member.Name);
    }

    protected void SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression, params Expression<Func<object>>[] additonal)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return;
        field = value;
        OnPropertyChanged(selectorExpression);
        foreach (var item in additonal)
            OnPropertyChanged(item);
    }
}

用法与Thomas的答案相同,只是您可以传递要通知的其他属性。这对于处理需要在网格中刷新的计算列是必要的。

private int _quantity;
private int _price;

public int Quantity 
{ 
    get { return _quantity; } 
    set { SetField(ref _quantity, value, () => Quantity, () => Total); } 
}
public int Price 
{ 
    get { return _price; } 
    set { SetField(ref _price, value, () => Price, () => Total); } 
}
public int Total { get { return _price * _quantity; } }

我有这个驱动存储在BindingList中的项的集合,通过DataGridView暴露。它消除了我对网格进行手动Refresh()调用的需要。

我提出了这个基类来实现可观察模式,几乎做了你所需要的(“自动”实现set和get)。我花了一个小时在这个原型上,所以它没有很多单元测试,但证明了这个概念。注意,它使用Dictionary<string, ObservablePropertyContext>来消除私有字段的需要。

  public class ObservableByTracking<T> : IObservable<T>
  {
    private readonly Dictionary<string, ObservablePropertyContext> _expando;
    private bool _isDirty;

    public ObservableByTracking()
    {
      _expando = new Dictionary<string, ObservablePropertyContext>();

      var properties = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList();
      foreach (var property in properties)
      {
        var valueContext = new ObservablePropertyContext(property.Name, property.PropertyType)
        {
          Value = GetDefault(property.PropertyType)
        };

        _expando[BuildKey(valueContext)] = valueContext;
      }
    }

    protected void SetValue<T>(Expression<Func<T>> expression, T value)
    {
      var keyContext = GetKeyContext(expression);
      var key = BuildKey(keyContext.PropertyName, keyContext.PropertyType);

      if (!_expando.ContainsKey(key))
      {
        throw new Exception($"Object doesn't contain {keyContext.PropertyName} property.");
      }

      var originalValue = (T)_expando[key].Value;
      if (EqualityComparer<T>.Default.Equals(originalValue, value))
      {
        return;
      }

      _expando[key].Value = value;
      _isDirty = true;
    }

    protected T GetValue<T>(Expression<Func<T>> expression)
    {
      var keyContext = GetKeyContext(expression);
      var key = BuildKey(keyContext.PropertyName, keyContext.PropertyType);

      if (!_expando.ContainsKey(key))
      {
        throw new Exception($"Object doesn't contain {keyContext.PropertyName} property.");
      }

      var value = _expando[key].Value;
      return (T)value;
    }

    private KeyContext GetKeyContext<T>(Expression<Func<T>> expression)
    {
      var castedExpression = expression.Body as MemberExpression;
      if (castedExpression == null)
      {
        throw new Exception($"Invalid expression.");
      }

      var parameterName = castedExpression.Member.Name;

      var propertyInfo = castedExpression.Member as PropertyInfo;
      if (propertyInfo == null)
      {
        throw new Exception($"Invalid expression.");
      }

      return new KeyContext {PropertyType = propertyInfo.PropertyType, PropertyName = parameterName};
    }

    private static string BuildKey(ObservablePropertyContext observablePropertyContext)
    {
      return $"{observablePropertyContext.Type.Name}.{observablePropertyContext.Name}";
    }

    private static string BuildKey(string parameterName, Type type)
    {
      return $"{type.Name}.{parameterName}";
    }

    private static object GetDefault(Type type)
    {
      if (type.IsValueType)
      {
        return Activator.CreateInstance(type);
      }
      return null;
    }

    public bool IsDirty()
    {
      return _isDirty;
    }

    public void SetPristine()
    {
      _isDirty = false;
    }

    private class KeyContext
    {
      public string PropertyName { get; set; }
      public Type PropertyType { get; set; }
    }
  }

  public interface IObservable<T>
  {
    bool IsDirty();
    void SetPristine();
  }

这是用法

public class ObservableByTrackingTestClass : ObservableByTracking<ObservableByTrackingTestClass>
  {
    public ObservableByTrackingTestClass()
    {
      StringList = new List<string>();
      StringIList = new List<string>();
      NestedCollection = new List<ObservableByTrackingTestClass>();
    }

    public IEnumerable<string> StringList
    {
      get { return GetValue(() => StringList); }
      set { SetValue(() => StringIList, value); }
    }

    public IList<string> StringIList
    {
      get { return GetValue(() => StringIList); }
      set { SetValue(() => StringIList, value); }
    }

    public int IntProperty
    {
      get { return GetValue(() => IntProperty); }
      set { SetValue(() => IntProperty, value); }
    }

    public ObservableByTrackingTestClass NestedChild
    {
      get { return GetValue(() => NestedChild); }
      set { SetValue(() => NestedChild, value); }
    }

    public IList<ObservableByTrackingTestClass> NestedCollection
    {
      get { return GetValue(() => NestedCollection); }
      set { SetValue(() => NestedCollection, value); }
    }

    public string StringProperty
    {
      get { return GetValue(() => StringProperty); }
      set { SetValue(() => StringProperty, value); }
    }
  }

这就是大规模的过度工程。这比仅仅以正确的方式进行要复杂得多,而且几乎没有任何好处。如果你的IDE支持代码片段(Visual Studio/MonoDevelop支持),那么你可以让这个实现变得非常简单。你需要输入的只是属性的类型和属性名。额外的三行代码将自动生成。