在 C# 中是否有一种好的强类型方法来执行 PropertyChanged 事件?
-
13-09-2019 - |
题
更改属性名称肯定是一个比较常见的事件,并且期望 Visual Studio 中的重命名功能能够处理所有必要的重命名,但 INotifyPropertyChanged 的 PropertyChanged 事件的属性名称除外。有没有更好的方法以某种方式将其强类型化,这样您就不需要记住手动重命名它?
解决方案
编辑:nameof
在c#到达6.耶
没有nameof
/ infoof
等;这是多讨论的,但它是它是什么。
有一种方法在.NET 3.5使用lambda表达式(和解析表达式树)这样做,但实际上它是不值得的开销。现在,我只是用绳子坚持(和单元测试,如果你坚决不打破它)。
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<T>(Expression<Func<Program, T>> 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_<PropertyName>
属性setter方法。如果编译器不再遵循以下命名约定,将码符。
在其他解决方案是从一个lambda表达式导出所述属性名称。
public static String GetPropertyNameFromLambdaExpression<TObject, TProperty>(
Expression<Func<TObject, TProperty>> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
例如
GetPropertyNameFromLambdaExpression<String, Int32>(s => s.Length)
如exspected将返回 “Length
”。代码的生产版本确实需要额外的检查和更好地融入代码的其余部分。例如,它可能使用类型推断为通用参数。
<强>更新强>
和还有第三溶液 - 你可以使用一个MethodBase.GetCurrentMethod()
属性getter或setter内部以获得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) 。不幸的是,谷歌搜索显示 它似乎对性能有重大影响. 。还有两件事需要考虑:
- JIT 内联可能会以意想不到的方式影响这一点。(stackoverflow.com/questions/616779/can-i-check-if-the-c-compiler-inlined-a-method-call)
- 理论上,对 MethodBase.GetCurrentMethod() 的 IL 调用可以简单地由运行时的 JIT 替换,使用 ldtoken 指令,然后调用 MethodBase.GetMethodFromHandle(),这会非常快。我想用户只是还没有表达出对此的需求。(msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ldtoken.aspx)
- 完全是我的观点,但我认为 C# 中拥有 fieldof() 和 methodof() 运算符会很好。我相信这将极大地提高需要这种能力的项目中代码分析/重构工具的可靠性。
不回答你的问题,但如果你用鼠标右键单击 - > Refactor->重命名一个属性,它可以重命名字符串匹配,以及包括符合您的属性的名称,任何字符串。
是的,它可以是一个有点危险。
您应该看看这个博客文章一>。它给你的能力做到这一点:
string propertyName = TypeHelper.GetPropertyName<User>(u => u.LastProjectCode);
PropertyInfo property1 = TypeHelper.GetProperty((SomeClass o) => o.InstanceProperty.Length);
PropertyInfo property2 = TypeHelper.GetProperty(() => SomeClass.StaticProperty.Length);
重命名在Visual Studio / ReSharper的/重构临应该为你工作呢。
在 PropertyChangedEventArgs 只需要一个构造函数,它需要的属性名称为字符串。所以基本上NO-利用INotifyPropertyChanged的意思是在一定程度上,无论是在你的架构高或低,你将有一个字符串和手动重命名工作。