zoukankan      html  css  js  c++  java
  • PostSharp 面向方面系统(转)

    PostSharp 是一个令人兴奋的项目,他结合了 MSBuild Task 和 MSIL Injection 技术,从另外一个角度实现 AOP 编程。试用过后你会感觉到其便利性,我们和以往基于 Dynamic Proxy 方式的 AOP 解决方案做个比较。
    • 由于采用 MSIL Injection,因此静态代码注入的执行效率要高于使用 Reflection Emit。
    • 使用 MSBuild Task,使得开发人员可以像使用编译器内置 Attribute 那样使用 AOP。
    • 可以拦截任意方法,而 Dynamic Proxy 方式的 AOP 往往采取继承方式来拦截 Virtual 方法。
    • 拥有更多的控制权。包括中断执行流程,修改参数和返回值等等。
    • 还可以拦截 Field Access、Exception 等操作。
    • 无需将对象创建代码改成 "new proxy()",更加透明。
    • 可以使用通配符进行多重拦截匹配。
    • 静态注入带来的问题更多的是注入代码的质量和调试复杂度。
    我们写一个简单的例子,看看效果。
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Reflection;
    using PostSharp.Laos;

    [Serializable]
    public class AopMethodAttribute : OnMethodBoundaryAspect
    {
      
    public override void OnEntry(MethodExecutionEventArgs eventArgs)
      
    {
        
    // 获取方法名称
        Console.WriteLine("OnEntry:{0}.{1}", eventArgs.Method.ReflectedType, eventArgs.Method);
        
        
    // 显示方法参数
        ParameterInfo[] ps = eventArgs.Method.GetParameters();
        
    object[] pv = eventArgs.GetArguments();

        
    for (int i = 0; i < ps.Length; i++)
        
    {
          Console.WriteLine(
    " {0}={1}", ps[i].Name, pv[i]);
        }

      }


      
    public override void OnExit(MethodExecutionEventArgs eventArgs)
      
    {
        Console.WriteLine(
    "OnExit");
      }

    }


    public class MyClass
    {
      [AopMethod]
      
    public int Test(int i)
      
    {
        Console.WriteLine(
    "Test:{0}", i);
        
    return i++;
      }

    }


    MyClass o 
    = new MyClass();
    o.Test(
    123);

    输出:
    OnEntry:MyClass.Int32 Test(Int32)
    i=123
    Test:123
    OnExit...

    整个过程非常简单:
    1. 创建一个继承自 OnMethodBoundaryAspect 的特性类,用于拦截方法。
    2. override 特定的方法,执行拦截操作。方法参数提供了各种操作能力。
    3. 向目标方法添加特性。
    4. 编译,执行。

    或许你会好奇,AopMethodAttribute 是如何被创建实例,并被执行的呢?

    1. 注意编译时 VS2005 Output 窗口的输出内容,你会看到如下内容。其实 PostSharp 提供了一个 MSBuild Task,在我们每次编译时会调用它执行一些注入操作。
    PostSharp 1.0 [1.0.4.162] - Copyright (c) Gael Fraiteur and Community, 2006.

    2. 用反编译工具看看注入后的方法内容。看完这些代码,就算我不说,你也能明白它是如何实现拦截的了。

    static MyClass()
    {
      
    if (!~PostSharp~Laos~Implementation.initialized)
      
    {
        
    throw new LaosNotInitializedException();
      }

      
    ~PostSharp~Laos~Implementation.~targetMethod~1 = methodof(MyClass.Test);
      
    ~PostSharp~Laos~Implementation.~aspect~1.RuntimeInitialize(~PostSharp~Laos~Implementation.~targetMethod~1);
    }


    public int Test(int i)
    {
      
    int returnValue;
      MethodExecutionEventArgs eventArgs;
      
    try
      
    {
        
    object[] arguments = new object[] { i };
        eventArgs 
    = new MethodExecutionEventArgs(methodof(MyClass.Test, MyClass), this, arguments);
        
    ~PostSharp~Laos~Implementation.~aspect~1.OnEntry(eventArgs);
        
    if (eventArgs.FlowBehavior == FlowBehavior.Return)
        
    {
          
    return (int) eventArgs.ReturnValue;
        }

        Console.WriteLine(
    "Test:{0}", i);
        
    int num = i++;
        returnValue 
    = num;
        eventArgs.ReturnValue 
    = returnValue;
        
    ~PostSharp~Laos~Implementation.~aspect~1.OnSuccess(eventArgs);
        returnValue 
    = (int) eventArgs.ReturnValue;
      }

      
    catch (Exception exception)
      
    {
        eventArgs.Exception 
    = exception;
        
    ~PostSharp~Laos~Implementation.~aspect~1.OnException(eventArgs);
        
    switch (eventArgs.FlowBehavior)
        
    {
          
    case FlowBehavior.Continue:
            
    return returnValue;

          
    case FlowBehavior.Return:
            
    return (int) eventArgs.ReturnValue;
        }

        
    throw;
      }

      
    finally
      
    {
        eventArgs.ReturnValue 
    = returnValue;
        
    ~PostSharp~Laos~Implementation.~aspect~1.OnExit(eventArgs);
        returnValue 
    = (int) eventArgs.ReturnValue;
      }

      
    return returnValue;
    }

    我们可以使用通配符拦截更多的内容,以简化我们的编码。
    [AopMethod(AttributeTargetMembers="T*")]
    public class MyClass
    {
      public int T1(int i)
      {
        Console.WriteLine("T1:{0}", i);
        return i++;
      }

      public void T2(string s)
      {
        Console.WriteLine("T2:{0}", s);
      }
    }

    我们继续写一点其它的例子,诸如拦截属性访问和异常。

     


    Field Aspect 
    [Serializable]
    public class AopPropertyAttribute : OnFieldAccessAspect
    {
      
    public override void OnGetValue(FieldAccessEventArgs eventArgs)
      {
        Console.WriteLine(
    "ExposedFieldValue:{0}; StoredFieldValue:{1}"
          eventArgs.ExposedFieldValue, eventArgs.StoredFieldValue);
      }

      
    public override void OnSetValue(FieldAccessEventArgs eventArgs)
      {
        Console.WriteLine(
    "ExposedFieldValue:{0}; StoredFieldValue:{1}"
          eventArgs.ExposedFieldValue, eventArgs.StoredFieldValue);
      }
    }

    public class MyClass
    {
      [AopProperty]
      
    private int x;

      
    public int X
      {
        
    get { return x; }
        
    set { x = value; }
      }
    }

    Exception Aspect 
    [Serializable]
    public class AopExceptionAttribute : OnExceptionAspect
    {
      
    public override void OnException(MethodExecutionEventArgs eventArgs)
      {
        Console.WriteLine(eventArgs.Exception.Message);
        eventArgs.FlowBehavior 
    = FlowBehavior.Return;
      }
    }

    public class MyClass
    {
      [AopException]
      
    public void Test(int i)
      {
        
    throw new Exception("Error");
      }
    }

    更详细的信息,请访问 PostSharp 网站,或者查阅其帮助文件。

    结论:

    1.方法执行的日志记录可以用AOP实现

    2.表之间的级联删除可以用AOP实现

    3.数据库操作日志可以用AOP记录(因为数据库提供商不提供它的日志,导致第三方数据库同步无法便利实现)

    ...

  • 相关阅读:
    GRYZ20211029模拟赛解题报告
    LG 题解 CF1545B AquaMoon and Chess
    GRYZ10.27模拟赛解题报告
    开博客通告
    民科吧编程赛 题解
    民科吧编程赛 试题
    四则运算开平方——对民科吧编程大赛题目的再探究
    The First
    机械手相机9点坐标标定-基于C#+EmguCV
    Emgucv图像处理工具
  • 原文地址:https://www.cnblogs.com/zjoch/p/1268442.html
Copyright © 2011-2022 走看看