65.9K
CodeProject 正在变化。 阅读更多。
Home

简洁的PropertyChanged实现

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.25/5 (7投票s)

2016 年 6 月 30 日

CPOL

1分钟阅读

viewsIcon

20495

节省键盘磨损:使用最少的按键实现INotifyPropertyChanged。

我讨厌PropertyChanged

……因为我很快就习惯了自动属性……

构建与UI交互的实体时,最痛苦的部分(至少对我来说)是必须创建参与INotifyPropertyChanged接口的属性。

你知道我指的是哪些

string _backingField;

public string MyProperty  
{
    get { return _backingField; }
    set 
    { 
          if (!string.Equals(value, _backingField)
          {
              _backingField = value;
              OnPropertyChanged("MyProperty");
          }
    }
}

你可以使用代码片段来提高添加这些属性的速度,但存在一些令人讨厌的限制,例如必须重命名备份字段以匹配命名约定,OnPropertyChanged传递的名称在重构时可能失效 *(除非你使用的是C#6.0/VS2015和“nameof()”),而且它过于冗长,最终会得到一堆你不需要的备份字段,而且看起来很丑陋。

如果它看起来像这样,不是更好吗?

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

上面的代码可以使用代码片段轻松生成,它整齐对齐,不需要备份字段,类型推断和泛型协同工作以使其具有强类型,并且重构属性名称不会破坏任何东西。

诀窍是什么?

表达式!GetValueSetValue方法的第一参数是一个Expression<Func<T>> - 这允许你将lambda表达式传递到该方法中(包含属性名称),并提供泛型类型上下文 (T)

剩下的诀窍是,备份字段实际上是一个Dictionary<string, object>,它存储所有属性的值。

        /// <summary>
        /// stores the values for the properties
        /// </summary>
        protected Dictionary<string, object> m_values = new Dictionary<string, object>();

        /// <summary>
        /// gets the value of the member specified in the member expression. 
        /// Generic type parameters should be inferred.
        /// </summary>
        /// <typeparam name="T">the property type</typeparam>
        /// <param name="memberExpression">lambda expression 
        /// referring to the property invoking this method</param>
        /// <returns>the value, or default(T)</returns>
        public T GetValue<T>(Expression<Func<T>> memberExpression)
        {
            var body = memberExpression.Body as MemberExpression;
            if (body != null)
            {
                object value;
                if (m_values.TryGetValue(body.Member.Name, out value))
                {
                    // return the value;
                    return (T)value;
                }
            }

            // return a default:
            return default(T);
        }

        /// <summary>
        /// sets the value
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memberExpression"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool SetValue<T>(Expression<Func<T>> memberExpression, T value)
        {
            // is the value different from the existing?
            if (EqualityComparer<T>.Default.Equals(value, GetValue(memberExpression)))
            {
                return false;
            }

            // fetch the name of the property:
            var body = memberExpression.Body as MemberExpression;
            if (body != null)
            {
                // set the value:
                m_values[body.Member.Name] = value;

                // raise the property-changed event
                OnPropertyChanged(body.Member.Name);
            }

            // return true for changed:
            return true;
        }
© . All rights reserved.