当前位置:  开发笔记 > 编程语言 > 正文

如何在C#中拦截方法调用?

如何解决《如何在C#中拦截方法调用?》经验,为你挑选了5个好方法。

对于给定的类,我希望有跟踪功能,即我想记录每个方法调用(方法签名和实际参数值)和每个方法退出(只是方法签名).

我如何做到这一点假设:

我不想为C#使用任何第三方AOP库,

我不想将重复的代码添加到我想要跟踪的所有方法中,

我不想更改类的公共API - 类的用户应该能够以完全相同的方式调用所有方法.

为了使问题更具体,让我们假设有3个类:

 public class Caller 
 {
     public static void Call() 
     {
         Traced traced = new Traced();
         traced.Method1();
         traced.Method2(); 
     }
 }

 public class Traced 
 {
     public void Method1(String name, Int32 value) { }

     public void Method2(Object object) { }
 }

 public class Logger
 {
     public static void LogStart(MethodInfo method, Object[] parameterValues);

     public static void LogEnd(MethodInfo method);
 }

如何调用Logger.LogStartLogger.LogEnd每次调用方法1方法2,而无需修改Caller.Call方法,没有明确地加入调用Traced.Method1Traced.Method2

编辑:如果我允许稍微更改Call方法,会有什么解决方案?



1> Jorge Córdob..:

C#不是面向AOP的语言.它有一些AOP功能,你可以模仿其他一些,但用C#制作AOP是很痛苦的.

我想方设法做你想做的事情,我找不到简单的办法.

据我了解,这是你想要做的:

[Log()]
public void Method1(String name, Int32 value);

为了做到这一点,你有两个主要选择

    从MarshalByRefObject或ContextBoundObject继承您的类,并定义一个继承自IMessageSink的属性.这篇文章有一个很好的例子.你必须考虑使用MarshalByRefObject,性能会像地狱一样下降,我的意思是,我说的是性能损失了10倍,所以在尝试之前要仔细考虑.

    另一种选择是直接注入代码.在运行时,意味着你将不得不使用反射"读取"每个类,获取其属性并注入适当的调用(为此我认为你不能使用Reflection.Emit方法,因为我认为Reflection.Emit不会不允许您在现有方法中插入新代码.在设计时,这将意味着创建一个CLR编译器的扩展,我真的不知道它是如何完成的.

最后一个选择是使用IoC框架.也许它不是完美的解决方案,因为大多数IoC框架通过定义允许方法挂钩的入口点来工作,但是,根据您想要实现的目标,这可能是一个公平的近似.


换句话说,'ouch'
第三种方法是使用`Reflection.Emit`在运行时生成基于aop代理的继承.这是[Spring.NET选择的方法](http://www.springframework.net/doc-latest/reference/html/aop.html#aop-introduction-proxies).但是,这将需要`Traced`上的虚拟方法,并且在没有某种IOC容器的情况下不太适合使用,所以我理解为什么这个选项不在你的列表中.
我应该指出,如果您具有一流的函数,那么可以像对待其他任何变量一样对待函数,并且可以使用一个“方法挂钩”来实现他想要的功能。
你的第二个选择基本上是"手写你需要的AOP框架的部分",这应该导致"哦等等也许我应该使用专门为解决我遇到的问题而不是下来的第三方选项-inveted,这里道路"

2> Antoine Aubr..:

实现这一目标的最简单方法可能是使用PostSharp.它根据您应用于它的属性在方法中注入代码.它允许您完全按照自己的意愿行事.

另一种选择是使用分析API在方法中注入代码,但这确实是硬核.


你也可以用ICorDebug注入东西,但这是超级邪恶的

3> Steen..:

如果你编写一个类 - 称之为Tracing - 实现IDisposable接口,你可以将所有方法体包装在一个

Using( Tracing tracing = new Tracing() ){ ... method body ...}

在Tracing类中,您可以分别在Tracing类中处理构造函数/ Dispose方法中跟踪的逻辑,以跟踪方法的进入和退出.这样:

    public class Traced 
    {
        public void Method1(String name, Int32 value) {
            using(Tracing tracer = new Tracing()) 
            {
                [... method body ...]
            }
        }

        public void Method2(Object object) { 
            using(Tracing tracer = new Tracing())
            {
                [... method body ...]
            }
        }
    }



4> plog17..:

您可以使用诸如Castle Windsor之类的DI容器的拦截功能来实现它.实际上,可以以这样的方式配置容器,即每个具有由特定属性修饰的方法的类都将被截获.

关于第3点,OP要求提供没有AOP框架的解决方案.我在下面的回答中假设应该避免的是Aspect,JointPoint,PointCut等.根据CastleWindsor的拦截文档,没有一个是完成所要求的.

根据属性的存在配置拦截器的通用注册:

public class RequireInterception : IContributeComponentModelConstruction
{
    public void ProcessModel(IKernel kernel, ComponentModel model)
    {
        if (HasAMethodDecoratedByLoggingAttribute(model.Implementation))
        {
            model.Interceptors.Add(new InterceptorReference(typeof(ConsoleLoggingInterceptor)));
            model.Interceptors.Add(new InterceptorReference(typeof(NLogInterceptor)));
        }
    }

    private bool HasAMethodDecoratedByLoggingAttribute(Type implementation)
    {
        foreach (var memberInfo in implementation.GetMembers())
        {
            var attribute = memberInfo.GetCustomAttributes(typeof(LogAttribute)).FirstOrDefault() as LogAttribute;
            if (attribute != null)
            {
                return true;
            }
        }

        return false;
    }
}

将创建的IContributeComponentModelConstruction添加到容器中

container.Kernel.ComponentModelBuilder.AddContributor(new RequireInterception());

你可以在拦截器本身做任何你想做的事情

public class ConsoleLoggingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.Writeline("Log before executing");
        invocation.Proceed();
        Console.Writeline("Log after executing");
    }
}

将logging属性添加到要记录的方法中

 public class Traced 
 {
     [Log]
     public void Method1(String name, Int32 value) { }

     [Log]
     public void Method2(Object object) { }
 }

请注意,如果只需要拦截某个类的某些方法,则需要对该属性进行一些处理.默认情况下,将截获所有公共方法.



5> 小智..:

如果您想无限制地跟踪方法(无代码改编,无AOP框架,无重复代码),请告诉我,您需要一些魔术...

认真地说,我解决了它,以实现在运行时工作的AOP框架。

您可以在这里找到:NConcern .NET AOP框架

我决定创建此AOP框架来响应这种需求。这是一个非常轻量的简单库。您可以在主页上看到记录器的示例。

如果您不想使用第三方组装,则可以浏览代码源(开放源代码),并复制文件Aspect.Directory.cs和Aspect.Directory.Entry.cs以适应您的需要。这些类允许在运行时替换您的方法。我只想请您尊重许可证。

我希望您能找到所需的内容或说服您最终使用AOP框架。

推荐阅读
Life一切安好
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有