使用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; }
答案 0 :(得分:4)
对我来说,除了简单的重构之外让编译器捕获错误的机会使得Exression方法更好。是否支持使用我缺少的原始字符串?
我同意并且在个人情况下,在大多数情况下,在我自己的代码中使用表达式方法。
但是,我知道有两个原因可以避免这种情况:
不太明显,特别是对经验不足的.NET开发人员而言。写作RaisePropertyChanged(() => this.MyProperty );
并不总是像RaisePropertyChanged("MyProperty");
那样明显,并且与框架样本等不匹配。
使用表达式会有一些性能开销。就个人而言,我并不认为这是一个有意义的原因,因为这通常用于数据绑定方案(由于反射使用已经很慢),但它可能是一个有效的问题。
答案 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.aspx和http://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));
}
}
}