首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在C#中拦截方法调用?

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

Stack Overflow用户
提问于 2008-08-25 09:00:24
回答 16查看 99.2K关注 0票数 156

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

假设存在以下情况,我该如何完成此操作:

我不想为C#,,

  • 使用任何第三方AOP库,我不想给我想要跟踪的所有方法添加重复的代码,

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

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

代码语言:javascript
复制
 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);
 }

如何为每个对Method1和Method2的调用调用Logger.LogStart和Logger.LogEnd,而不修改Caller.Call方法,也不显式地将调用添加到Traced.Method1和Traced.Method2

编辑:如果允许我稍微更改调用方法,那么解决方案是什么?

EN

回答 16

Stack Overflow用户

回答已采纳

发布于 2008-08-25 09:31:41

C#不是一种面向面向方面的语言。它有一些面向方面的特性,你可以模仿其他的特性,但是用C#进行面向方面的编程是很痛苦的。

我一直在寻找做你想做的事情的方法,但我发现没有简单的方法去做。

据我所知,这就是你想要做的:

代码语言:javascript
复制
[Log()]
public void Method1(String name, Int32 value);

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

  1. 从MarshalByRefObject或ContextBoundObject继承您的类,并定义从IMessageSink继承的属性。This article有一个很好的例子。然而,你必须考虑到使用MarshalByRefObject的性能会像地狱一样下降,我是认真的,我说的是10倍的性能损失,所以在尝试之前要仔细考虑。
  2. 另一种选择是直接注入代码。在运行时,这意味着您必须使用反射来“读取”每个类,获取它的属性并注入适当的调用(就这一点而言,我认为您不能使用Reflection.Emit方法,因为我认为Reflection.Emit不允许您在已存在的方法中插入新代码)。在设计时,这将意味着为CLR编译器创建一个扩展,老实说,我完全不知道它是如何完成的。

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

票数 72
EN

Stack Overflow用户

发布于 2008-08-25 09:41:13

要实现这一点,最简单的方法可能是使用PostSharp。它根据你应用的属性在你的方法中注入代码。它可以让你做你想做的事情。

另一种选择是使用profiling API在方法内部注入代码,但这是真正的核心。

票数 49
EN

Stack Overflow用户

发布于 2016-12-14 03:07:25

您可以使用Castle Windsor等DI容器的Interception特性来实现它。实际上,可以这样配置容器:具有由特定属性修饰的方法的每个类都将被拦截。

关于第三点,OP要求一个没有AOP框架的解决方案。在下面的回答中,我假设应该避免的是方面、JointPoint、PointCut等。根据Interception documentation from CastleWindsor的说法,这些都不是完成所要求的内容所必需的。

基于属性的存在配置侦听器的一般注册:

代码语言:javascript
复制
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添加到容器中

代码语言:javascript
复制
container.Kernel.ComponentModelBuilder.AddContributor(new RequireInterception());

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

代码语言:javascript
复制
public class ConsoleLoggingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.Writeline("Log before executing");
        invocation.Proceed();
        Console.Writeline("Log after executing");
    }
}

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

代码语言:javascript
复制
 public class Traced 
 {
     [Log]
     public void Method1(String name, Int32 value) { }

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

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

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25803

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档