C# 重构INotifyPropertyChanged Setters
我讨厌写这样的INPC设置器:C# 重构INotifyPropertyChanged Setters,c#,wpf,inotifypropertychanged,getter-setter,C#,Wpf,Inotifypropertychanged,Getter Setter,我讨厌写这样的INPC设置器: public string Label { get {return _label;} set { if (_label == value) return; _label = value; NotifyPropertyChanged(() => Label); } } public string Label { get {return _label;} set { Se
public string Label
{
get {return _label;}
set
{
if (_label == value) return;
_label = value;
NotifyPropertyChanged(() => Label);
}
}
public string Label
{
get {return _label;}
set
{
SetProperty(() => Label, ref _label, value);
}
}
我想像重构INPC一样重构字段的设置;我想将表达式
(可能还有backing字段)传入如下内容:
public string Label
{
get {return _label;}
set
{
if (_label == value) return;
_label = value;
NotifyPropertyChanged(() => Label);
}
}
public string Label
{
get {return _label;}
set
{
SetProperty(() => Label, ref _label, value);
}
}
。。。下面是我在基类中提出的实现:
public virtual void SetProperty<T>(Expression<Func<T>> expression, ref T field, T value)
{
if (Equals(field, value)) return;
field = value;
NotifyPropertyChanged(expression);
}
public virtual void SetProperty(表达式,ref T字段,T值)
{
if(等于(字段、值))返回;
字段=值;
NotifyPropertyChanged(表达式);
}
。。。而且它似乎是有效的——至少它是编译的。我的问题是:我是否遗漏了什么?路过裁判会完成我的目标吗 是的,会的。不过,您仍然会对编写私有支持字段和重复部分感到有点厌倦:
private int _foo;
public int Foo { get{return _foo;}, set { SetProperty("Foo", ref _foo, value); } }
你不可能轻易逃脱
INPC的工作完全基于三件事:
- 需要属性的名称
- 需要提出这一事件
- 还可以选择在某处实际存储值
是一个很好的开始,但是对于自定义对象,它可能要求您在许多数据类中重写if(Equals(field,value))
和Equals
,这并不总是一个好主意,尤其是在WPF和绑定中。向WPF传递Equals/HashCode重写数据对象时,您必须小心,并准备好看到WPF有时会混淆,有时可能无法正确更新绑定(即,它可能无法注意到对象已被替换为新对象,并将一些绑定边界保留给旧实例)。最好对SetProperty进行重载,该重载将使用GetHashCode
;不过是化妆品IEqualityComparer
- 在旧的.Net版本中,可以从调用堆栈自动获取名称,但代价非常昂贵。例如,这就是为什么许多框架(如XPO)故意在某个时候停止这样做,并要求您手动提供名称。然而,自从.NET4.5以来,“呼叫者信息”要便宜得多。请参阅:您将能够删除“字符串propertyName”。这会花一点钱,但对你来说可能很方便
- 说到框架,请参见Caliburn Micro及其属性ChangedBase类。您可能会发现它非常有用(Caliburn在nuget上提供),或者,至少。它使用上述调用方信息作为默认值,还具有一些很好的功能,如布尔标志来临时禁用通知(
)。在某些情况下非常有用IsNotifying
- 还有一个商业
实用程序,它能够。。自动生成所有INPC实现。将PostSharp util作为后期构建步骤附加,它从属性中读取属性,如果发现某些属性被标记为to-be-a-INPC,则它将重写程序集的IL并将INPC实现添加到其中。有什么好处<代码>[INPC]公共int Foo{get;set;}并完成。然而,请注意,PostSharp是一家商业公司。如果你找到一个IL-weaver,你也许可以自己做这件事,可能是基于Mono的Cecil。或者你也可以找到一个免费的util。对不起,我不记得太多了PostSharp
SetProperty(()=>Label,somepocclass.Label,value)
,它使用表达式以编译安全的方式查找属性名称(无字符串!重构/重命名有效)。如果您决定坚持使用ref,那么通过一些额外的工作,您可以获得()=>标签
表达式,对其进行分析并提取名称,然后将其转换并重写为val=>\u Label=val delegate,并对每个字段缓存该委托,然后使用该缓存委托而不是使用ref参数,结果是:set{SetProperty()=>标签,值);}
这几乎和当前的C#规格一样“酷”。但是:这样的SetProperty变得非常复杂,转换表达式并不轻松,但几乎可以一次性完成,但委托缓存不是“免费”的,您必须对它们进行缓存,因为计算转换后的委托过于繁重。我试过,我见过其他人试过,从我的观察来看,大多数人都退出了。许多同事认为这是一种魔力,如果有什么不对劲,他们就不会去碰它。很酷,很有趣,但是你不需要C大师来调试/修补简单的功能,比如INPC
编辑2:
更简单的方法是,获取一些T4模板来为您生成代码。这也是非常流行的处理方法。或者更确切地说,就像3-4年前一样。我知道周围有些人真的很喜欢T4。不知怎么的,我没有。我想写PHP或ASP的第一个版本,而不是Net。。但是不要在意我在这里的意见——看看它们,自己试试看。很多人喜欢它
顺便说一句,抱歉搞砸了-我刚刚想起了一些关于IL编织的事情:
- 对于初学者,请参阅,内容非常丰富,涵盖了所有基础知识和更多动态代理,Cecil的il编织,(…)
- “自由”号