在C#中有一个很好的强类型方法来做PropertyChanged事件吗?

更改属性名称并期望Visual Studio中的重命名function必须处理所有必需的重命名,除了INotifyPropertyChanged的PropertyChanged事件的属性名称之外,它必须是一个常见的事件。 有没有更好的方法以某种方式获得强类型,所以你不需要记得手动重命名它?

编辑: nameof到达c#6。耶!


没有nameof / infoof等; 这是很多讨论,但它就是这样。

有一种方法可以在.NET 3.5中使用lambda表达式(并解析表达式树),但实际上它不值得开销。 现在,我只是坚持使用字符串(如果您决定不打破它,则进行unit testing)。


 using System; using System.ComponentModel; using System.Linq.Expressions; using System.Reflection; class Program : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; static void Main() { var p = new Program(); p.PropertyChanged += (s, a) => Console.WriteLine(a.PropertyName); p.Name = "abc"; } protected void OnPropertyChanged(Expression> property) { MemberExpression me = property.Body as MemberExpression; if (me == null || me.Expression != property.Parameters[0] || me.Member.MemberType != MemberTypes.Property) { throw new InvalidOperationException( "Now tell me about the property"); } var handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(me.Member.Name)); } string name; public string Name { get{return name;} set { name = value; OnPropertyChanged(p=>p.Name); } } } 

C#5似乎有一个解决方案。 使用可与参数一起使用的CallerMemberName属性 ( 网络上的一个示例 )。

 class Employee : INotifyPropertyChanged { private string _Name; public string Name { get { return _Name; } set { _Name = value; RaisePropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged([CallerMemberName] string caller = "") { var temp = PropertyChanged; if ( temp != null ) { temp( this, new PropertyChangedEventArgs( caller ) ); } } } 

最简单的解决方案是查看堆栈跟踪并完全删除对该属性的每个显式引用。

 public String Name { get { return this.name; } set { if (value != this.name) { this.RaisePropertyChanging(); this.name = value; this.RaisePropertyChanged(); } } } private String name = null; private void RaisePropertyChanged() { String propertyName = new StackTrace().GetFrame(1).GetMethod().Name.SubString(4); PropertyChangedEventHandler handler = this.PropertyChanged; if (handler != null) { handler(new PropertyChangedEventArgs(propertyName)); } } 

代码通过caling方法的堆栈跟踪派生属性名称 – 即名为set_的属性setter方法。 如果编译器不再遵循此命名约定,则代码会中断。

另一种解决方案是从lambda表达式派生属性名称。

 public static String GetPropertyNameFromLambdaExpression( Expression> expression) { return ((MemberExpression)expression.Body).Member.Name; } 

例如

 GetPropertyNameFromLambdaExpression(s => s.Length) 

将返回“ Length ”作为预期。 代码的生产版本确实需要额外的检查并更好地集成到其余代码中。 例如,可以对generics参数使用类型推断。

UPDATE

还有第三种解决方案 – 您可以在属性getter或setter中使用MethodBase.GetCurrentMethod()来获取setter或getter方法的名称。

 public String Name { get { return this.name; } set { if (value != this.name) { String propertyName = MethodBase.GetCurentMethod().Name.SubString(4); this.RaisePropertyChanging(propertyName); this.name = value; this.RaisePropertyChanged(propertyName); } } } private String name = null; 

理论上,您可以在属性设置器中使用MethodBase.GetCurrentMethod()。Name.Substring(4)。 不幸的是,Google搜索显示它似乎对性能产生了重大影响 。 还有两件事需要考虑:

  • JIT内联可以以意想不到的方式影响这一点。 (stackoverflow.com/questions/616779/can-i-check-if-the-c-compiler-inlined-a-method-call)
  • 从理论上讲,对ActionBase.GetCurrentMethod()的IL调用可以在运行时通过ldtoken指令然后调用MethodBase.GetMethodFromHandle()来简单地替换为JIT,这将非常快。 我猜用户只是没有表达对此的需求。 (msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ldtoken.aspx)
  • 完全是我的意见,但我认为在C#中使用fieldof()和methodof()运算符会很好。 我相信它会大大提高需要这种能力的项目中代码分析/重构工具的可靠性。

不是您的问题的答案,但如果您右键单击 – >重构 – >重命名属性,它也可以重命名匹配的字符串,包括与您的属性名称匹配的任何字符串。

是的,它可能有点危险。

你应该看一下这篇博文 。 它使您能够执行此操作:

 string propertyName = TypeHelper.GetPropertyName(u => u.LastProjectCode); PropertyInfo property1 = TypeHelper.GetProperty((SomeClass o) => o.InstanceProperty.Length); PropertyInfo property2 = TypeHelper.GetProperty(() => SomeClass.StaticProperty.Length); 

Visual Studio / Resharper / Refactor Pro中的重命名应该适合您。

PropertyChangedEventArgs只接受一个构造函数,它需要将属性名称作为字符串。 所以基本上没有利用INotifyPropertyChanged意味着在某种程度上,无论你的架构是高还是低,你都必须使用字符串和手动重命名。