什么是使用INotifyPropertyChanged的时候指定属性名的最好方法?
大多数例子硬编码的属性名称作为PropertyChanged事件的说法。 我想使用MethodBase.GetCurrentMethod.Name.Substring(4),但我有点不安的反射开销。
什么是使用INotifyPropertyChanged的时候指定属性名的最好方法?
大多数例子硬编码的属性名称作为PropertyChanged事件的说法。 我想使用MethodBase.GetCurrentMethod.Name.Substring(4),但我有点不安的反射开销。
不要忘了一两件事: PropertyChanged
事件主要是通过将使用反射来获取命名属性的值组件消耗。
最明显的例子就是数据绑定。
当你火PropertyChanged
事件,传递属性作为参数的名称,你应该知道, 本次活动的用户很可能通过调用使用反射 ,例如, GetProperty
(至少第一次,如果它使用的高速缓存PropertyInfo
),然后GetValue
。 这最后一次通话是属性getter方法,其成本比更多的动态调用(MethodInfo.Invoke) GetProperty
只查询的元数据。 (请注意,数据绑定依赖于整体TypeDescriptor事情-但默认实现使用反射。)
所以,当射击的PropertyChanged当然是使用硬编码的属性名称比使用反射来动态获取属性的名称更有效率,但恕我直言,以平衡你的想法是很重要的。 在某些情况下,性能开销是不是关键的,你可以从某种上强类型的事件触发机制中获益。
以下是我有时使用C#3.0中,当表演将不会成为问题:
public class Person : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return this.name; }
set
{
this.name = value;
FirePropertyChanged(p => p.Name);
}
}
private void FirePropertyChanged<TValue>(Expression<Func<Person, TValue>> propertySelector)
{
if (PropertyChanged == null)
return;
var memberExpression = propertySelector.Body as MemberExpression;
if (memberExpression == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
注意使用表达式树来获得属性的名称,并使用lambda表达式作为的Expression
:
FirePropertyChanged(p => p.Name);
这里的反射开销几乎矫枉过正尤其是INotifyPropertyChanged的被调用了很多 。 这是最好的只是硬编码值,如果你能。
如果你不关心性能,那么我看看下面所提到的各种接洽并挑选需要的编码量最少。 如果你能做些什么来完全消除需要显式调用那将是最好的(如AOP)。
在.NET 4.5(C#5.0)有一个叫做新属性- CallerMemberName它有助于避免硬编码的属性名称防止错误的发生,如果开发人员决定更改属性的名称,这里是一个例子:
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void OnPropertyChanged([CallerMemberName]string propertyName="")
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
参与使用表达式树的性能损失是由于表达式树的重复分辨率。
下面的代码仍然使用表达式树,因此具有可重构友好和模糊处理友好随之而来的优点,但实际上是大约40%的速度(非常粗略的测试)比通常的技术 - 其由newing了PropertyChangedEventArgs的每改变通知对象。
这是更快,因为我们缓存静态PropertyChangedEventArgs对象的每个属性避免了表达式树的性能损失。
有一件事我还没有这样做 - 我打算添加一些代码,检查调试版本,对所提供的PropertChangedEventArgs对象的属性名称中正在使用它的属性相匹配 - 在此代码它仍然是当下可以为开发者提供了错误的对象。
看看这个:
public class Observable<T> : INotifyPropertyChanged
where T : Observable<T>
{
public event PropertyChangedEventHandler PropertyChanged;
protected static PropertyChangedEventArgs CreateArgs(
Expression<Func<T, object>> propertyExpression)
{
var lambda = propertyExpression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
var propertyInfo = memberExpression.Member as PropertyInfo;
return new PropertyChangedEventArgs(propertyInfo.Name);
}
protected void NotifyChange(PropertyChangedEventArgs args)
{
if (PropertyChanged != null)
{
PropertyChanged(this, args);
}
}
}
public class Person : Observable<Person>
{
// property change event arg objects
static PropertyChangedEventArgs _firstNameChangeArgs = CreateArgs(x => x.FirstName);
static PropertyChangedEventArgs _lastNameChangeArgs = CreateArgs(x => x.LastName);
string _firstName;
string _lastName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
NotifyChange(_firstNameChangeArgs);
}
}
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
NotifyChange(_lastNameChangeArgs);
}
}
}
罗马:
我说你甚至不需要“人”的参数 - 因此,完全通用的代码片段类似下面应该做的:
private int age;
public int Age
{
get { return age; }
set
{
age = value;
OnPropertyChanged(() => Age);
}
}
private void OnPropertyChanged<T>(Expression<Func<T>> exp)
{
//the cast will always succeed
MemberExpression memberExpression = (MemberExpression) exp.Body;
string propertyName = memberExpression.Member.Name;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
......不过,我宁愿坚持字符串参数有条件验证的调试版本。 约什 - 史密斯张贴在这一个很好的示例:
这实现INotifyPropertyChanged甲基类
干杯:)菲利普
是的,我看到你的建议的功能的使用和简单性,但考虑到运行成本,由于反射的时候,是啊,这是一个坏主意,我用的这个方案是有一个代码段正确地添加到利用时间优势和错误在所有Notifyproperty事件触发写的属性。
我能想到的另一个非常不错的方法是
用方面自动执行INotifyPropertyChanged
AOP:面向方面编程
在CodeProject上漂亮的文章: INotifyPropertyChanged的的AOP实现
你可能在这个讨论中interessted
“最佳实践:如何实现INotifyPropertyChanged的吧?”
太。
如果没有被irrevelant,硬编码和反思之间,我的选择是: notifypropertyweaver 。
此Visual Studio的包让你有反射(可维护性,可读性,...),而要失去perfs的好处。
其实,你只需要实现INotifyPropertyChanged,并在编译添加所有“通知的东西”。
如果你想充分优化你的代码,这也是完全parametrable。
例如,对于notifypropertyweaver,你将不得不在你的编辑器的代码:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
}
代替 :
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string givenNames;
public string GivenNames
{
get { return givenNames; }
set
{
if (value != givenNames)
{
givenNames = value;
OnPropertyChanged("GivenNames");
OnPropertyChanged("FullName");
}
}
}
private string familyName;
public string FamilyName
{
get { return familyName; }
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged("FamilyName");
OnPropertyChanged("FullName");
}
}
}
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
public virtual void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
对于讲法语的人: 提高你的代码的可读性和简化你的生活notifypropertyweaver
此外,我们发现那里得到一个方法名在调试工作与不同发布版本的问题:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/244d3f24-4cc4-4925-aebe-85f55b39ec92
(我们使用的代码是不是在你建议的方式恰好反映,但它使我们确信硬编码的属性名称是最快和最可靠的解决方案。)
与基于反射方法的问题是,它是相当昂贵的,而不是非常快。 当然,它更灵活,对重构那么脆。
然而,它确实会损害性能,尤其是当事情被频繁调用。 将StackFrame方法,还(我相信)在CAS的问题(如限制信任级别,如XBAP)。 这是最好的硬编码。
如果您寻找在WPF快速,灵活的特性通知有一个解决办法 - 使用DependencyObject的:)那它是专为。 如果你不希望采取的依赖,或担心线程关联问题,将属性名称为常数,热潮! 你的好。
你可能想避免INotifyPropertyChanged的干脆。 它增加了不必要的簿记代码到您的项目。 考虑使用更新控件.NET来代替。
另一种方法: http://www.codeproject.com/Articles/450688/Enhanced-MVVM-Design-w-Type-Safe-View-Models-TVM
由于C#6.0还有一个nameof()关键字将在编译时进行评估,所以它会一直表现为硬编码值,并针对与通知属性不匹配的保护。
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string info)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
public string SelectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem != value)
{
_selectedItem = value;
NotifyPropertyChanged(nameof(SelectedItem));
}
}
}
private string _selectedItem;
我做了这样的事情,一旦做实验,从内存中它的工作确定,并取消了需要硬编码字符串中的所有属性名称。 性能可能是问题,如果你建立一个高容量服务器应用程序,你可能永远不会发现其中的差别在桌面上。
protected void OnPropertyChanged()
{
OnPropertyChanged(PropertyName);
}
protected string PropertyName
{
get
{
MethodBase mb = new StackFrame(1).GetMethod();
string name = mb.Name;
if(mb.Name.IndexOf("get_") > -1)
name = mb.Name.Replace("get_", "");
if(mb.Name.IndexOf("set_") > -1)
name = mb.Name.Replace("set_", "");
return name;
}
}
看看这篇博客文章: http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code