Unity Behaviors for Interception

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     set
11     {
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 Members
30 
31   public event PropertyChangedEventHandler PropertyChanged;
32 
33   #endregion
34 }

上面的视图模型可以优化,创建一个ViewModelBase的基类,实现了INotifyPropertyChanged接口,并提供了OnPropertyChanged函数。但对于ViewModel的开发人员来说仍旧需要在各处添加触发PropertyChanged事件的函数或代码。我们可以通过Unity的VirtualMethodInterceptor,编写一个NotifyPropertyChangedBehavior实现属性变化的通知。看一个示例:

  1 // <summary>
  2 /// 属性变化行为
  3 /// </summary>
  4 public sealed class NotifyPropertyChangedBehavior : IInterceptionBehavior
  5 {
  6   /// <summary>
  7   /// 添加事件函数信息
  8   /// </summary>
  9   private static readonly MethodInfo AddEventMethodInfo = typeof(INotifyPropertyChanged).GetEvent(“PropertyChanged”).GetAddMethod();
 11 
 12   /// <summary>
 13   /// 删除事件函数信息
 14   /// </summary>
 15   private static readonly MethodInfo RemoveEventMethodInfo = typeof(INotifyPropertyChanged).GetEvent(“PropertyChanged”).GetRemoveMethod();
 17 
 18   /// <summary>
 19   /// 属性变化事件
 20   /// </summary>
 21   private event PropertyChangedEventHandler PropertyChanged;
 22 
 23   /// <summary>
 24   /// 构造函数
 25   /// </summary>
 26   public NotifyPropertyChangedBehavior() { }
 27 
 28   /// <summary>
 29   /// 是否为属性Setter
 30   /// </summary>
 31   /// <param name=”input”>输入</param>
 32   /// <returns>为属性Setter</returns>
 33   private static Boolean IsPropertySetter(IMethodInvocation input)
 34   {
 35     return input.MethodBase.IsSpecialName && input.MethodBase.Name.StartsWith(“set_”);
 36   }
 37 
 38   /// <summary>
 39   /// 添加事件订阅
 40   /// </summary>
 41   /// <param name=”input”>输入</param>
 42   /// <param name=”getNext”>下一个行为</param>
 43   /// <returns>函数返回值</returns>
 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   /// <summary>
 54   /// 删除事件订阅
 55   /// </summary>
 56   /// <param name=”input”>输入</param>
 57   /// <param name=”getNext”>下一个行为</param>
 58   /// <returns>函数返回值</returns>
 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   /// <summary>
 69   /// 拦截属性设置
 70   /// </summary>
 71   /// <param name=”input”>输入</param>
 72   /// <param name=”getNext”>下一个行为</param>
 73   /// <returns>函数返回值</returns>
 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   /// <summary>
 89   /// 是否将执行
 90   /// </summary>
 91   public Boolean WillExecute
 92   {
 93     get { return true; }
 94   }
 95 
 96   /// <summary>
 97   /// 获得需要的接口遍历器
 98   /// </summary>
 99   /// <returns>接口遍历器</returns>
100   public IEnumerable<Type> GetRequiredInterfaces()
101   {
102     return new[] { typeof(INotifyPropertyChanged) };
103   }
104 
105   /// <summary>
106   /// 调用
107   /// </summary>
108   /// <param name=”input”>输入</param>
109   /// <param name=”getNext”>下一个行为</param>
110   /// <returns>函数返回值</returns>
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   #endregion
129 }
130 
131 public class MainViewModel
132 {
133   public MainViewModel() { }
134 
135   public virtual String Name { get; set; }
136 }
137 
138 IUnityContainer unityContainer = new UnityContainer();
139 
140 unityContainer.AddNewExtension<Interception>();
141 unityContainer.RegisterType<MainViewModel>(new Interceptor<VirtualMethodInterceptor>(), new InterceptionBehavior(new NotifyPropertyChangedBehavior()));
142 
143 MainViewModel viewModel = unityContainer.Resolve<MainViewModel>();
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是下一个行为,整个行为附加是一个链式调用。

posted @ 2012-08-10 11:36  junchu25  阅读(437)  评论(0编辑  收藏  举报