实现INotifyPropertyChanged的不同方式之间有什么区别?

时间:2019-05-10 15:34:55

标签: c# mvvm inotifypropertychanged

这些天来,我一直在尝试在我的UWP应用中实现MVVM模式,而没有其他框架作为学习练习。尽管我仍在努力了解INotifyPropertyChanged接口的实现,但是目前我正在大量阅读它。我遇到了非常不同的方法,但是我无法理解它们之间的区别。

这是csharpcorner建议的here

        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在this的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));
    }
  }
}

有人可以解释这些差异,并告诉我这些实现之一是否更好?特别是:

  • Tomtom版本中的Dictionary有什么用途?

  • 在John的版本中,SetProperty重载有什么作用?

  • 为什么前两个示例没有最后一个示例那样的Get方法?没必要吗?

  • John为什么要添加NotificationBase<T>类?其他人错过了重要的事情吗?

谢谢。

1 个答案:

答案 0 :(得分:3)

Tomtom版本似乎是尝试使用字典而不是字段;这是 flexible -有点像ExpandoObject--但效率低下,涉及很多额外的对象(字典,键, plus 不论树的结构如何)字典使用)花费大量的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);
    }
}

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