首页 > 解决方案 > 实现 INotifyPropertyChanged 的​​不同方式之间有什么区别?

问题描述

这些天来,我一直在尝试在我的 UWP 应用程序中实现 MVVM 模式,而无需额外的框架作为学习练习。虽然我仍然难以理解INotifyPropertyChanged接口的实现,所以我现在读了很多。我遇到了非常不同的方法,但我无法理解它们之间的区别。

这就是 csharpcorner在这里的建议:

        public class BaseModel : INotifyPropertyChanged  
    {  
        public event PropertyChangedEventHandler PropertyChanged;  

        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)  
        {  
            if (object.Equals(storage, value)) return false;  
            storage = value;  
            this.OnPropertyChaned(propertyName);  
            return true;  
        }  

        private void OnPropertyChaned(string propertyName)  
        {  
            var eventHandler = this.PropertyChanged;  
            if (eventHandler != null)  
                eventHandler(this, new PropertyChangedEventArgs(propertyName));  
        }  
    }  

这就是 John Shews 在msdn的这篇博文中的做法:

public class NotificationBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        // SetField (Name, value); // where there is a data member
        protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] String property 
           = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value)) return false;
            field = value;
            RaisePropertyChanged(property);
            return true;
        }

        // SetField(()=> somewhere.Name = value; somewhere.Name, value) 
        // Advanced case where you rely on another property
        protected bool SetProperty<T>(T currentValue, T newValue, Action DoSet,
            [CallerMemberName] String property = null)
        {
            if (EqualityComparer<T>.Default.Equals(currentValue, newValue)) return false;
            DoSet.Invoke();
            RaisePropertyChanged(property);
            return true;
        }

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

    public class NotificationBase<T> : NotificationBase where T : class, new()
    {
        protected T This;

        public static implicit operator T(NotificationBase<T> thing) { return thing.This; }

        public NotificationBase(T thing = null)
        {
            This = (thing == null) ? new T() : thing;
        }
}

这就是@Tomtom 对我之前关于SO 的一个问题的建议:

public abstract class NotifyBase : INotifyPropertyChanged
{
  private readonly Dictionary<string, object> mapping;

  protected NotifyBase()
  {
    mapping = new Dictionary<string, object>();
  }

  protected void Set<T>(T value, [CallerMemberName] string propertyName = "")
  {
    mapping[propertyName] = value;
    OnPropertyChanged(propertyName);
  }

  protected T Get<T>([CallerMemberName] string propertyName = "")
  {
    if(mapping.ContainsKey(propertyName))
      return (T)mapping[propertyName];
    return default(T);
  }

  public event PropertyChangedEventHandler PropertyChanged;

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

有人可以解释这些差异并告诉我这些实现中的一个是否更好?尤其:

提前致谢。

标签: c#mvvminotifypropertychanged

解决方案


Tomtom 版本似乎是尝试使用字典而不是字段;这是灵活的——有点像ExpandoObject——但它的效率可能出奇的低,涉及大量额外的对象(字典、键,以及字典使用的任何树结构)和大量的 CPU 周期用于不断查找。

如果您有很多潜在领域(我的意思是数百个),但您通常一次只使用 3 个,那么这可能是一个有效的解决方案。同样,如果结构是完全动态的(可能基于您无法控制的输入数据,可能与 配对ICustomTypeDescriptor),那么它可能很有用。但是,字典和CallerMemberName建议组合旨在与属性一起使用,这使得这......非常奇怪。

但总的来说:我会使用更简单的ref字段版本。这没有Get方法的原因是调用者已经可以直接使用该字段。

所以:如果你想要这种代码,我会使用:

public class MyType : BaseModel
{
    private string _name;
    public string Name {
        get => _name;
        set => SetProperty(ref _name, value);
    }

    private int _id;
    public string Id {
        get => _id;
        set => SetProperty(ref _id, value);
    }
}

我猜 Tomtom 版本试图避免必须声明字段,即

public class MyType : BaseModel
{
    public string Name {
        get => GetProperty<string>();
        set => SetProperty<string>(value);
    }

    public string Id {
        get => Get<int>();
        set => SetProperty<int>(value);
    }
}

但是......是的,不要那样做。除了其他所有内容之外,这最终会装箱所有值类型。田野很好...


推荐阅读