RaisePropertyChanged待会再实现。先来学习一下之前没有碰到过的PostSharp的API。args.GetCurrentValue获取当前的位置值,但是它还没有把值value放到args.Value。因此,这里把它存储在oldValue变量中再合适不过了。args.Value返回即将到来的位置值。args.ProceedSetValue指示PostSharp允许继续set操作。
如果属性值发生了变化,那么我们就触发属性改变的事件。看一下传入的实参,args.Instance返回的是属性所在的对象(比如,NameViewModel类的实例),它应该是一个实现了INotifyPropertyChanged的类。args.LocationName返回被拦截的属性名,比如可能是FirstName或LastName。
当属性更改的通知发出之后,遍历所有指定的导出属性(如FullName),并为这些属性调用RaisePropertyChanged方法。下面我们完成最后这个切面并写完RaisePropertyChanged方法。在该方法中,你期望找到传入的实例对象上的PropertyChanged事件,并使用传入的位置名触发那个事件。然而,只有纯粹的一个对象object传入,所以必须借助反射来处理:
private void RaisePropertyChanged(object instance, string propertyName) { var type = instance.GetType(); var propertyChanged = type.GetField("PropertyChanged", BindingFlags.Instance|BindingFlags.NonPublic); var handler = propertyChanged.GetValue(instance) as PropertyChangedEventHandler; handler(instance,new PropertyChangedEventArgs(propertyName)); }这个方法中没使用任何PostSharp API,只有反射的API。反射会检索实例instance的类型,从该类型中可以找到PropertyChanged事件字段。使用那个字段可以调用事件。
这里使用发射,是因为从类外面触发事件的唯一方式就是反射了。这样做并不好,因为反射是一个缓慢的过程,这样编写切面的话意味着属性每次改变时都会执行反射。此外,如果这个切面用在一个没有PropertyChanged事件的类上,那么就会报错。(解决办法请关注后面的教程,特别是PostSharp的CompileTimeValidate功能)
小结这节我们覆盖了一个新的拦截类型:拦截属性和字段(位置)。和拦截方法一样,位置拦截切面扮演着getter/setter和处理getting/setting代码之间的中间人。
C#中的属性提供了简明的方式编写getter/setter方法,可以拦截方法的工具也可以拦截属性(比如Castle DynamicProxy)。但PostSharp为位置提供了一个特殊的类,该API可以同时为属性和字段服务。和方法拦截一样,我们可以继续执行get/setca操作,也可以获得关于位置的信息(比如字段名和属性名),实例对象等等。
这节也引入了一个新的AOP工具——PropertyChanged.Fody,这个工具很专一,只做一件事,不像PostSharp和Castle DynamicProxy是通用框架。
现在,我们已经可以编写拦截方法、边界方法、拦截位置的切面了。但是学习AOP不仅仅是数量(可以少写代码),而且还有质量。下一篇我们看下如何将单元测试和切面结合起来。
posted @