.net propertychange通知处理程序 - 字符串与表达式

时间:2009-11-02 20:43:41

标签: wpf expression inotifypropertychanged

使用WPF让我成为了INotifyPropertyChanged的粉丝。我喜欢使用一个带有表达式的助手,并将该名称作为字符串返回(参见下面的示例代码)。然而,在非常熟练的程序员看到的许多应用程序中,我看到处理字符串raw的代码(参见下面的第2个示例)。精通我的意思是知道如何使用表达式的MVP类型。

对我来说,除了简单的重构之外让编译器捕获错误的机会使得Exression方法更好。是否支持使用我缺少的原始字符串?

干杯, Berryl

表达式助手示例:

public static string GetPropertyName<T>(Expression<Func<T, object>> propertyExpression)
    {
        Check.RequireNotNull<object>(propertyExpression, "propertyExpression");
        switch (propertyExpression.Body.NodeType)
        {
            case ExpressionType.MemberAccess:
                return (propertyExpression.Body as MemberExpression).Member.Name;
            case ExpressionType.Convert:
                return ((propertyExpression.Body as UnaryExpression).Operand as MemberExpression).Member.Name;
        }
        var msg = string.Format("Expression NodeType: '{0}' does not refer to a property and is therefore not supported", 
            propertyExpression.Body.NodeType);
        Check.Require(false, msg);
        throw new InvalidOperationException(msg);
    }

原始字符串示例代码(在某些ViewModelBase类型类中):

        /// <summary>
    /// Warns the developer if this object does not have
    /// a public property with the specified name. This 
    /// method does not exist in a Release build.
    /// </summary>
    [Conditional("DEBUG"), DebuggerStepThrough]

    public void VerifyPropertyName(string propertyName) {
        // Verify that the property name matches a real,  
        // public, instance property on this object.
        if (TypeDescriptor.GetProperties(this)[propertyName] == null) {
            string msg = "Invalid property name: " + propertyName;

            if (ThrowOnInvalidPropertyName) throw new Exception(msg);
            else Debug.Fail(msg);
        }
    }

    /// <summary>
    /// Returns whether an exception is thrown, or if a Debug.Fail() is used
    /// when an invalid property name is passed to the VerifyPropertyName method.
    /// The default value is false, but subclasses used by unit tests might 
    /// override this property's getter to return true.
    /// </summary>
    protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

5 个答案:

答案 0 :(得分:4)

  

对我来说,除了简单的重构之外让编译器捕获错误的机会使得Exression方法更好。是否支持使用我缺少的原始字符串?

我同意并且在个人情况下,在大多数情况下,在我自己的代码中使用表达式方法。

但是,我知道有两个原因可以避免这种情况:

  1. 不太明显,特别是对经验不足的.NET开发人员而言。写作RaisePropertyChanged(() => this.MyProperty );并不总是像RaisePropertyChanged("MyProperty");那样明显,并且与框架样本等不匹配。

  2. 使用表达式会有一些性能开销。就个人而言,我并不认为这是一个有意义的原因,因为这通常用于数据绑定方案(由于反射使用已经很慢),但它可能是一个有效的问题。

答案 1 :(得分:2)

使用TypeDescriptor方法的好处是它支持基于ICustomTypeDescriptor实现的动态属性方案,其中实现可以动态地为正在描述的类型创建动态属性元数据。考虑一个DataSet,其“属性”由其填充的结果集确定。

这是表达式不能提供的东西,因为它基于实际的类型信息(一件好事)而不是字符串。

答案 2 :(得分:1)

我花了更多时间在这上面比我预期的更多,但确实找到了一个安全/可重构性和性能良好组合的解决方案。良好的背景阅读和使用反射的替代解决方案是here。我更喜欢Sacha Barber的解决方案(背景here

我们的想法是为一个参与变更通知的属性使用表达式帮助器,但只对它进行一次命中,方法是在视图模型中存储生成的PropertyChangedEventArgs。例如:

private static PropertyChangedEventArgs mobilePhoneNumberChangeArgs =
        ObservableHelper.CreateArgs<CustomerModel>(x => x.MobilePhoneNumber);

HTH,
Berryl

答案 3 :(得分:0)

堆栈遍历很慢,lambda表达式甚至更慢。我们有类似于众所周知的lambda表达式的解决方案,但几乎与字符串文字一样快。看到 http://zamboch.blogspot.com/2011/03/raising-property-changed-fast-and-safe.html

答案 4 :(得分:0)

在.net 4.5中引入了CallerMemberName属性 此属性只能附加到可选字符串参数,如果函数调用中调用者未使用该参数,则调用者的名称将在字符串参数中传递

这样就不需要在引发PropertyChanged事件时指定属性名称,因此它可以用于重构,并且因为更改是在编译时完成的,所以性能没有区别。

以下是实施示例,可在http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute.aspxhttp://msdn.microsoft.com/en-us/library/hh534540.aspx找到更多信息

    public class DemoCustomer : INotifyPropertyChanged
    {
        string _customerName
        public string CustomerName
        {
            get { return _customerNameValue;}
            set
            {
                if (value != _customerNameValue)
                {
                    _customerNameValue = value;
                    NotifyPropertyChanged();
                }
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }