Implementing INotifyPropertyChanged - does a better way exist?

前端 未结 30 3199
感情败类
感情败类 2020-11-21 05:23

Microsoft should have implemented something snappy for INotifyPropertyChanged, like in the automatic properties, just specify {get; set; notify;} I

30条回答
  •  半阙折子戏
    2020-11-21 05:51

    Here is a Unity3D or non-CallerMemberName version of NotifyPropertyChanged

    public abstract class Bindable : MonoBehaviour, INotifyPropertyChanged
    {
        private readonly Dictionary _properties = new Dictionary();
        private static readonly StackTrace stackTrace = new StackTrace();
        public event PropertyChangedEventHandler PropertyChanged;
    
        /// 
        ///     Resolves a Property's name from a Lambda Expression passed in.
        /// 
        /// 
        /// 
        /// 
        internal string GetPropertyName(Expression> property)
        {
            var expression = (MemberExpression) property.Body;
            var propertyName = expression.Member.Name;
    
            Debug.AssertFormat(propertyName != null, "Bindable Property shouldn't be null!");
            return propertyName;
        }
    
        #region Notification Handlers
    
        /// 
        ///     Notify's all other objects listening that a value has changed for nominated propertyName
        /// 
        /// 
        internal void NotifyOfPropertyChange(string propertyName)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
    
        /// 
        ///     Notifies subscribers of the property change.
        /// 
        /// The type of the property.
        /// The property expression.
        internal void NotifyOfPropertyChange(Expression> property)
        {
            var propertyName = GetPropertyName(property);
            NotifyOfPropertyChange(propertyName);
        }
    
        /// 
        ///     Raises the  event directly.
        /// 
        /// The  instance containing the event data.
        internal void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
    
        #endregion
    
        #region Getters
    
        /// 
        ///     Gets the value of a property
        /// 
        /// 
        /// 
        /// 
        internal T Get(Expression> property)
        {
            var propertyName = GetPropertyName(property);
            return Get(GetPropertyName(property));
        }
    
        /// 
        ///     Gets the value of a property automatically based on its caller.
        /// 
        /// 
        /// 
        internal T Get()
        {
            var name = stackTrace.GetFrame(1).GetMethod().Name.Substring(4); // strips the set_ from name;
            return Get(name);
        }
    
        /// 
        ///     Gets the name of a property based on a string.
        /// 
        /// 
        /// 
        /// 
        internal T Get(string name)
        {
            object value = null;
            if (_properties.TryGetValue(name, out value))
                return value == null ? default(T) : (T) value;
            return default(T);
        }
    
        #endregion
    
        #region Setters
    
        /// 
        ///     Sets the value of a property whilst automatically looking up its caller name.
        /// 
        /// 
        /// 
        internal void Set(T value)
        {
            var propertyName = stackTrace.GetFrame(1).GetMethod().Name.Substring(4); // strips the set_ from name;
            Set(value, propertyName);
        }
    
        /// 
        ///     Sets the value of a property
        /// 
        /// 
        /// 
        /// 
        internal void Set(T value, string propertyName)
        {
            Debug.Assert(propertyName != null, "name != null");
            if (Equals(value, Get(propertyName)))
                return;
            _properties[propertyName] = value;
            NotifyOfPropertyChange(propertyName);
        }
    
        /// 
        ///     Sets the value of a property based off an Expression (()=>FieldName)
        /// 
        /// 
        /// 
        /// 
        internal void Set(T value, Expression> property)
        {
            var propertyName = GetPropertyName(property);
    
            Debug.Assert(propertyName != null, "name != null");
    
            if (Equals(value, Get(propertyName)))
                return;
            _properties[propertyName] = value;
            NotifyOfPropertyChange(propertyName);
        }
    
        #endregion
    }
    

    This code enables you to write property backing fields like this:

      public string Text
        {
            get { return Get(); }
            set { Set(value); }
        }
    

    Furthermore, in resharper if you create a pattern/search snippet you can then also automate you're workflow by converting simple prop fields into the above backing.

    Search Pattern:

    public $type$ $fname$ { get; set; }
    

    Replace Pattern:

    public $type$ $fname$
    {
        get { return Get<$type$>(); }
        set { Set(value); }
    }
    

提交回复
热议问题