在C#中是否有一种很好的强类型方法来执行属性更改事件?
更改属性的名称并期望Visual Studio中的重命名功能处理所有必要的重命名必须是一个比较常见的事件,INotifyPropertyChanged的PropertyChanged事件的属性名称除外。有没有更好的方法可以让它强类型化,这样您就不需要记住手动重命名它了?这不是您问题的答案,但是如果您右键单击->重构->重命名属性,它也可以重命名匹配的字符串,包括与属性名称匹配的任何字符串在C#中是否有一种很好的强类型方法来执行属性更改事件?,c#,inotifypropertychanged,strong-typing,C#,Inotifypropertychanged,Strong Typing,更改属性的名称并期望Visual Studio中的重命名功能处理所有必要的重命名必须是一个比较常见的事件,INotifyPropertyChanged的PropertyChanged事件的属性名称除外。有没有更好的方法可以让它强类型化,这样您就不需要记住手动重命名它了?这不是您问题的答案,但是如果您右键单击->重构->重命名属性,它也可以重命名匹配的字符串,包括与属性名称匹配的任何字符串 是的,可能有点危险 只接受一个构造函数,它要求属性名作为字符串。因此,本质上不使用INotifyProper
是的,可能有点危险 只接受一个构造函数,它要求属性名作为字符串。因此,本质上不使用INotifyPropertyChanged意味着在某种程度上,无论您的体系结构是高还是低,您都必须使用字符串并手动重命名 Edit:
nameof
到达c#6。耶
没有
nameof
/infoof
等;这是很多人讨论过的,但事实就是这样
有一种方法可以使用.NET3.5中的lambda表达式(并解析表达式树)来实现,但实际上它不值得花费这么多开销。现在,我只会坚持使用字符串(如果您决定不破坏它,还可以使用单元测试)
使用系统;
使用系统组件模型;
使用System.Linq.Expressions;
运用系统反思;
类程序:INotifyPropertyChanged{
公共事件属性更改事件处理程序属性更改;
静态void Main(){
var p=新程序();
p、 PropertyChanged+=(s,a)=>Console.WriteLine(a.PropertyName);
p、 Name=“abc”;
}
受保护的void OnPropertyChanged(表达式属性){
MemberExpression me=property.Body作为MemberExpression;
if(me==null | | me.Expression!=property.Parameters[0]
||me.Member.MemberType!=MemberTypes.Property){
抛出新的InvalidOperationException(
“现在告诉我有关财产的情况”);
}
var handler=PropertyChanged;
如果(handler!=null)handler(this,
新属性ChangedEventArgs(me.Member.Name));
}
字符串名;
公共字符串名{
获取{返回名称;}
设置{
名称=值;
OnPropertyChanged(p=>p.Name);
}
}
}
最简单的解决方案是查看堆栈跟踪并完全删除对属性的所有显式引用
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\uu
的属性设置器方法)派生属性名称。如果编译器不再遵循此命名约定,代码将中断
另一种解决方案是从lambda表达式派生属性名
public static String GetPropertyNameFromLambdaExpression<TObject, TProperty>(
Expression<Func<TObject, TProperty>> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
理论上,可以从属性设置器中使用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()操作符会很好。我相信在需要这种能力的项目中,它将极大地提高代码分析/重构工具的可靠性
- 你应该看看这个。它使您能够做到这一点:
string propertyName = TypeHelper.GetPropertyName<User>(u => u.LastProjectCode);
PropertyInfo property1 = TypeHelper.GetProperty((SomeClass o) => o.InstanceProperty.Length);
PropertyInfo property2 = TypeHelper.GetProperty(() => SomeClass.StaticProperty.Length);
stringpropertyname=TypeHelper.GetPropertyName(u=>u.LastProjectCode);
PropertyInfo property1=TypeHelper.GetProperty((SomeClass o)=>o.InstanceProperty.Length);
PropertyInfo property2=TypeHelper.GetProperty(()=>SomeClass.StaticProperty.Length);
在VisualStudio/Resharper/Refactor Pro中进行重命名应该适合您。C#5似乎有一个解决方案。使用可与参数()一起使用的
甚至在评论中重命名也是危险的。我在一个相当大的项目中弄乱了一堆XML文档,因为我假设此功能会将范围限制在重命名的代码元素上/中的注释上。我可能只会坚持使用字符串,但为了好玩,您能给出一个关于如何解析表达式树的简短代码示例吗?为什么要使用
expression
和p=>p.Name
而不是expression
和()=>Name
,但发布的版本清楚地表明,我们正在实例上寻找一个属性,而不仅仅是随机的。在基本视图模型类上指定表达式将不允许子类提升其属性。这就是为什么表达式更适合于此目的。不再是真的!现在您可以使用nameof。这个解决方案比简单地将属性名设置为字符串更脆弱。这看起来很有趣。但是,如果不检查文档,我就无法完全理解调用SubString的目的。GetMethod().Name是否返回奇怪的内容?属性由两个方法实现。公共MyType MyProperty{get;set;}实现为公共void集_MyProperty(MyType值){}和公共MyType get_MyProperty()。因此,您必须从返回的方法名称中删除set_u和get_u,以获取属性的名称。(根据请求/注释添加的示例)看看本文。如果您创建了一个实现INotifyPropertyChanged的基类,然后从中派生,也许您可以使用它。有关实现INotifyPropertyChanged的编译器检查方法,请参阅。避免将属性名作为魔术字符串。
GetPropertyNameFromLambdaExpression<String, Int32>(s => s.Length)
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;
string propertyName = TypeHelper.GetPropertyName<User>(u => u.LastProjectCode);
PropertyInfo property1 = TypeHelper.GetProperty((SomeClass o) => o.InstanceProperty.Length);
PropertyInfo property2 = TypeHelper.GetProperty(() => SomeClass.StaticProperty.Length);
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 ) );
}
}
}