使用反射拦截方法调用或属性更改

Posted

技术标签:

【中文标题】使用反射拦截方法调用或属性更改【英文标题】:Intercept Method Invocation or Property Change with Reflection 【发布时间】:2016-06-28 06:13:04 【问题描述】:

我正在尝试创建一个通用类,该类将在调用方法或访问或更改属性时触发事件。它也可能会触发事件以响应正在采取的其他更改或操作,但目前仅此而已。

为了做到这一点,我想拦截每个方法调用和每个属性访问/更改,但我无法确切知道我正在处理哪些方法。没有给定的接口来定义我将使用的每个泛型类型T,所以我必须使用反射。这是我的设想(Trigger<T> 是类,genericT 类型):

public Trigger()

    this.generic = default(T);

    foreach (MethodInfo m in generic.GetType().GetMethods())
    
        // This is pseudocode, since I can't just set MethodInfo to a new method
        m = delegate()
            
                m.Invoke(this.generic, null);
                if (MethodCalled != null)
                    MethodCalled(this, eventArgs /*imagine these are valid EventArgs*/);
            ;
    

我意识到我把问题简单化了。首先,我必须处理参数。其次,您不能像那样以编程方式覆盖方法。第三,我什至还没有开始研究房产。另外,我必须只为对象而不是整个类型更改这些东西,所以我也不确定它是如何工作的。

我已经完成了研究,但我发现的只是令人困惑的内容。我意识到我应该以某种方式使用 AOP,但除了 OOP 和过程编程之外我从未做过任何其他事情,所以我相当迷失在这个密集的知识丛林中。听起来我需要使用 PostSharp 或 Unity,但我仍然不知道在 looking at all this 和 this 和 these two 以及 @ 987654328@(所有单独的链接,每个单词)。

有没有更简单的方法来做到这一点?我什至可以在不使用接口或预定义类的情况下做到这一点吗?

泛型使我的问题特别复杂。如果我可以从T 继承一个类,然后使用代理来捕获其方法调用和属性访问/更改,那么事情可能会简单一点,尽管我仍然缺乏对 AOP 的基本理解来做到这一点.您可以提供的任何帮助将不胜感激。如果可能,请以初学者的水平写下您的答案(尽管我对 OOP 非常了解,就像我说的,我对 AOP 的第一件事一无所知)。

【问题讨论】:

我研究了一下这个问题,发现它涉及到代码编织。代码编织正在改变事物,因此首先对任何方法的调用都被重新路由到其他编织的代码来完成它的事情。 Spring.NET 已经和其他人一样做到了这一点。因此,如果您真的想继续编写自己的代码,您应该研究已经这样做的开源项目来学习。 @JohnPeters 我不想编写自己的框架或代理。我想做的——正如 AOP 的术语所说,正如我通过 Spring.NET 学到的(感谢你)——就是让我的 Trigger<T> 班级担任顾问。它将为从T 类型的内部对象调用的所有方法提供后方法返回建议,每个Trigger<T> 对象都将包含该对象,它会从Trigger<T> 触发一个带有方法信息的事件(与属性相同,是否获取或设置它们),或类似的东西。但是,即使在阅读了 AOP 之后,我也不知道从哪里开始。 【参考方案1】:

无需求助于使用 post-bulid IL 编织的完整 AOP 框架,您可以使用 Castle 的 DynamicProxy 并创建一个拦截器。你可以在网上找到很多教程:

Simple AOP Short tutorial on CodeProject This extensive one。

为了让你的拦截器工作,你需要确保你的泛型类的方法和属性是virtual。这允许 DynamicProxy 的运行时编织代码生成包装您的类的代理。

【讨论】:

【参考方案2】:

您可以使用NConcern 来做这件事,这是一个我积极工作的新开源 AOP 框架。

public class Trigger<T> : Aspect

    static public event EventArgs MethodCalled;
    static private Trigger<T> m_Singleton = new Trigger<T>();

    //Auto weaving aspect
    static Trigger()
    
        Aspect.Weave<Trigger<T>>(method => method.ReflectedType == typeof(T));
    

    public IEnumerable<IAdvice> Advise(MethodInfo method)
    
        //define an advice to trigger only when method execution not failed
        yield return Advice.Basic.After.Returning(() => 
        
            if (MethodCalled != null)
            
                MethodCalled(this, null);
            
        );
    


public class A

    public void Test()
    
    


int main(string[] args)

    Trigger<A>.MethodCalled += ...
    new A().Test();

您可以在此处找到类似的示例代码源:Example of observation pattern implemented with NConcern

NConcern AOP 框架是一个在运行时工作的轻量级框架。它与代码注入一起工作,通过继承避免工厂/代理。它允许您通过在方法之前/之后或围绕方法注入您可以使用简单委托、ILGenerator 或表达式树 (linq) 创建的代码来向类添加方面。它可以处理密封类、密封方法、虚拟方法或显式/隐式接口实现。

在我的示例中,我创建了一个派生自 Aspect 的类(抽象类)。

当一个类从Aspect派生时,它必须通过返回一个Advice实例来实现Advise方法(Before/After/After.Returning/After.Throwing或Around)。每个都可以使用 Delegate 或 Expression 创建,以定义您在方法拦截时需要执行的操作。

public class MyAspect : IAspect

    //this method is called initially (not on interception) to rewrite method body.
    public IEnumerable<IAdvice> Advise(MethodInfo method)
    
        //this block of code means that method will be rewrite to execute a write method name to console before original code only for public methods
        if (method.IsPublic)
        
            yield return Advice.Basic.Before(() => Console.WriteLine(method.Name));
        
    

用法

//attach myaspect to A class. All methods of A will be passed to Advise method to process methods rewriting.
Aspect.Weave<MyAspect>(method => method.ReflectedType == typeof(A));

//detach myaspect from A class. All methods will be rewrite to give back original code.
Aspect.Release<MyAspect>(method => method.ReflectedType == typeof(A));

【讨论】:

以上是关于使用反射拦截方法调用或属性更改的主要内容,如果未能解决你的问题,请参考以下文章

反射常见方法

反射-优化及程序集等(用委托的方式调用需要反射调用的方法(或者属性字段),而不去使用Invoke方法)

java反射

java基础——反射

Java的反射机制

java反射的作用及使用详解