C#优雅的实现INotifyPropertyChanged接口

INotifyPropertyChanged接口在WPF或WinFrom程序中使用还是经常用到,常用于通知界面属性变更。标准写法如下:

class NotifyObject : INotifyPropertyChanged { private int number; public int Number { get { return number; } set { number = value; OnPropertyChanged("Number"); } } private string text; public string Text { get { return text; } set { text = value; OnPropertyChanged("Text"); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName = "") { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } }

这种写法的两个问题是

对属性名使用了字符串硬编码,容易写错,也不方便重构

冗余代码,如果属性较多的时候写得想吐

 我在博客文章使用CallerMemberName简化InotifyPropertyChanged的实现中介绍了通过Caller Information解决属性名称字符串硬编码的问题。但是仍然不能解决冗余代码的问题。对于第二个问题,往往是通过AOP的方式实现,一般的实现方式有两种:

通过代理对象封装

通过编译期间代码注入的方式实现

我增在项目中使用过DynamicObject封装来实现过,主要原理是用实现一个PocoNotifyWrapper的DynamicObject类,托管其属性的读写动作,并附加IntofyPropertyChanged接口实现。

这种方式是动态的AOP了,是一个通用的方式,并且扩展性比较器,可以通过继承PocoNotifyWrapper来实现多态。用它做ViewMode层还是比较方便。

不过一个不大好的地方是DynamicObject是丢失了属性类型信息的,有时绑定时会出错(如将一个TextBox的Text绑定到一个封装后的Int类型对象时会不进行自动类型转换)。要解决它还需要实现一些其它的接口,实现起来还是有些复杂的,并且项目进度比较紧张,没有太多时间来完善它。

另外一种方式是通过编译期间代码注入方式来实现:

最开始见的是PostSharp的一个实现: http://doc.postsharp.net/inotifypropertychanged-add。不过PostSharp是收费的,后来也逐渐由了许多其它的免费的解决方案。本文这里介绍的是一个开源的解决方案:Fody。

使用它非常简单,首先通过Nuget安装库:PM> Install-Package PropertyChanged.Fody。然后在需要实现属性通知的类上加一个[ImplementPropertyChanged]即可:

  [ImplementPropertyChanged]   public class Person   {     public string GivenNames { get; set; }     public string FamilyName { get; set; }     public string FullName => string.Format("{0} {1}", GivenNames, FamilyName);   }

编译后生成的代码如下:

public class Person : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; string givenNames; public string GivenNames { get { return givenNames; } set { if (value != givenNames) { givenNames = value; OnPropertyChanged("GivenNames"); OnPropertyChanged("FullName"); } } } 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)); } } }

基本上对代码没有注入,不需要安装插件,也不影响调试,实现非常简单,非常方便。

需要注意的是,如果实现了INotifyPropertyChanged接口,即使没有[ImplementPropertyChanged]标记,默认也会注入。

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对易知道(ezd.cc)的支持。如果你想了解更多相关内容请查看下面相关链接

推荐阅读

    foreach的用法c语言和c#

    foreach的用法c语言和c#,数组,遍历,本文目录foreach的用法c语言和c#详细讲解foreach循环的用法C#中的foreach 怎么用啊foreach用法C#中的f

    params 是什么意思,c#里

    params 是什么意思,c#里,参数,数组,本文目录params 是什么意思,c#里params.add跟params.put有区别吗C#中ref,out和params有什么区别发送请

    C# 基于StackExchange.Redis.dll利用Redis实现分布式Session

    C# 基于StackExchange.Redis.dll利用Redis实现分布式Session,令牌,客户端,最近在研发一款O2O产品,考虑到分布式架构的需要,以前那一套.NET的

    c#中 (ToolStrip

    c#中 (ToolStrip,窗体,控件,本文目录c#中 (ToolStrip)控件是做什么用的winform窗体问题 toolstrip怎么重用,每个窗体都要用到同样的工具栏窗体

    C#取得DataTable最大值、最小值

    C#取得DataTable最大值、最小值,最大值,最小值,C#取得DataTable最大值、最小值int max=int.Parse((dtItemsAll.Compute("Max(CPITEMS_SOR

    C#如何打印|cctv5体育节目表

    C#如何打印|cctv5体育节目表,,C#如何打印最省事的办法,引用word对象,再把word弹出来用word打印,比较适合你这样刚学的,还不容易出错。cctv5体

    C#中的listview控件怎么用

    C#中的listview控件怎么用,控件,第一个,C#中的listview控件怎么用比如你在窗体上放了一个ListView,教你一些简单的操作。listView1.Columns