Unity提供了自带的拦截器,如果你并不想编写繁琐的拦截器可以选择编写更轻量的方式Behavior。拦截器的应用场景有不少,比如一些数据访问框架,它们的核心概念是AOP。通过创建一个继承于类型的代理类型,并重写它的virtual函数将拦截器置入其中。前置处理函数负责打开数据库连接、启动事务,后置处理器负责提交事务、关闭数据库连接。Unity的VirtualMethodInterceptor就可以帮助我们完成这个功能。
如果你熟悉WPF/Silverlight,那么一定了解这两年流行的MVVM。WPF/Silverlight的数据绑定基于DataContext类型是一个DependencyObject还是一个实现了INotifyPropertyChanged接口的类型。当绑定类型的属性发生改变,数据绑定机制会主动将变化更新到UI。由于所有ViewModel都需要实现INotifyPropertyChanged接口,并且需要在每个属性的setter上显示的产生属性变化的通知:
1 public class MainViewModel : INotifyPropertyChanged 2 { 3 private String m_name; 4 5 public MainViewModel() { } 6 7 public virtual String Name 8 { 9 get { return m_name; }10 set11 {12 if (!String.Equals(m_name, value))13 {14 m_name = value;15 16 OnPropertyChanged(“Name”);17 }18 }19 }20 21 private void OnPropertyChanged(String propertyName)22 {23 PropertyChangedEventHandler handler = PropertyChanged;24 25 if (handler != null)26 handler(this, new PropertyChangedEventArgs(propertyName));27 }28 29 #region INotifyPropertyChanged Members30 31 public event PropertyChangedEventHandler PropertyChanged;32 33 #endregion34 }
上面的视图模型可以优化,创建一个ViewModelBase的基类,实现了INotifyPropertyChanged接口,并提供了OnPropertyChanged函数。但对于ViewModel的开发人员来说仍旧需要在各处添加触发PropertyChanged事件的函数或代码。我们可以通过Unity的VirtualMethodInterceptor,编写一个NotifyPropertyChangedBehavior实现属性变化的通知。看一个示例:
1 //2 /// 属性变化行为 3 /// 4 public sealed class NotifyPropertyChangedBehavior : IInterceptionBehavior 5 { 6 ///7 /// 添加事件函数信息 8 /// 9 private static readonly MethodInfo AddEventMethodInfo = typeof(INotifyPropertyChanged).GetEvent(“PropertyChanged”).GetAddMethod(); 11 12 ///13 /// 删除事件函数信息 14 /// 15 private static readonly MethodInfo RemoveEventMethodInfo = typeof(INotifyPropertyChanged).GetEvent(“PropertyChanged”).GetRemoveMethod(); 17 18 ///19 /// 属性变化事件 20 /// 21 private event PropertyChangedEventHandler PropertyChanged; 22 23 ///24 /// 构造函数 25 /// 26 public NotifyPropertyChangedBehavior() { } 27 28 ///29 /// 是否为属性Setter 30 /// 31 /// 输入 32 ///为属性Setter 33 private static Boolean IsPropertySetter(IMethodInvocation input) 34 { 35 return input.MethodBase.IsSpecialName && input.MethodBase.Name.StartsWith(“set_”); 36 } 37 38 ///39 /// 添加事件订阅 40 /// 41 /// 输入 42 /// 下一个行为 43 ///函数返回值 44 private IMethodReturn AddEventSubscription(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 45 { 46 var subscriber = (PropertyChangedEventHandler)input.Arguments[0]; 47 48 this.PropertyChanged += subscriber; 49 50 return input.CreateMethodReturn(null); 51 } 52 53 ///54 /// 删除事件订阅 55 /// 56 /// 输入 57 /// 下一个行为 58 ///函数返回值 59 private IMethodReturn RemoveEventSubscription(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 60 { 61 var subscriber = (PropertyChangedEventHandler)input.Arguments[0]; 62 63 this.PropertyChanged -= subscriber; 64 65 return input.CreateMethodReturn(null); 66 } 67 68 ///69 /// 拦截属性设置 70 /// 71 /// 输入 72 /// 下一个行为 73 ///函数返回值 74 private IMethodReturn InterceptPropertySet(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 75 { 76 var propertyName = input.MethodBase.Name.Substring(4); 77 var returnValue = getNext()(input, getNext); 78 var subscribers = PropertyChanged; 79 80 if (subscribers != null) 81 subscribers(input.Target, new PropertyChangedEventArgs(propertyName)); 82 83 return returnValue; 84 } 85 86 #region IInterceptionBehavior Members 87 88 ///89 /// 是否将执行 90 /// 91 public Boolean WillExecute 92 { 93 get { return true; } 94 } 95 96 ///97 /// 获得需要的接口遍历器 98 /// 99 ///接口遍历器 100 public IEnumerableGetRequiredInterfaces()101 {102 return new[] { typeof(INotifyPropertyChanged) };103 }104 105 /// 106 /// 调用107 /// 108 /// 输入109 /// 下一个行为110 ///函数返回值 111 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)112 {113 // 如果为添加事件114 if (input.MethodBase == AddEventMethodInfo)115 return AddEventSubscription(input, getNext);116 117 // 如果为删除事件118 if (input.MethodBase == RemoveEventMethodInfo)119 return RemoveEventSubscription(input, getNext);120 121 // 设置属性122 if (IsPropertySetter(input))123 return InterceptPropertySet(input, getNext);124 125 return getNext()(input, getNext);126 }127 128 #endregion129 }130 131 public class MainViewModel132 {133 public MainViewModel() { }134 135 public virtual String Name { get; set; }136 }137 138 IUnityContainer unityContainer = new UnityContainer();139 140 unityContainer.AddNewExtension();141 unityContainer.RegisterType (new Interceptor (), new InterceptionBehavior(new NotifyPropertyChangedBehavior()));142 143 MainViewModel viewModel = unityContainer.Resolve ();144 145 ((INotifyPropertyChanged)viewModel).PropertyChanged += new PropertyChangedEventHandler((sender, e) => Console.WriteLine(e.PropertyName));146 147 viewModel.Name = “hello, world”;
上面的示例可以看到MainViewModel被附加了PropertyChanged行为,开发人员不再需要为视图模型的属性变化编写大量重复的代码。一个拦截行为需要实现IInterceptionBehavior的三个定义:WillExecute属性、GetRequiredInterfaces函数、Invoke函数。
WillExecute:定义当前行为是否将被执行,开发人员可以根据不同情况编写相应逻辑。
GetRequiredInterfaces:返回需要被附加的接口,比如MainViewModel并没有实现INotifyPropertyChanged,但是被拦截后创建的代理类型被附加了INotifyPropertyChanged接口。
Invoke:拦截后的真正调用,getNext是下一个行为,整个行为附加是一个链式调用。