debugging 每隔一个方法调用动态调用一个特定方法

2ledvvac  于 2023-01-09  发布在  其他
关注(0)|答案(1)|浏览(129)

我使用的是.NET 4.8,在Windows窗体下。我在一个项目中,在不同的类中,有一定数量的声明方法。我希望这些类中的每个方法,在每个方法执行的一开始,就调用另一个特定的方法。我的意思是,如果我在每个方法体的第一行,手动添加所需的代码,来调用另一个特定的方法。
为什么我想这样做?:我写了一个特殊的方法来打印我感兴趣的调试信息,这个方法包括调用者成员的信息,但是它并不局限于这种信息。
然后,当我在调试模式下开发任何应用程序时,我希望在项目中的每个其他方法调用时自动调用此方法(如果可能,也在属性getter/setter中调用),我只是非常好奇是否存在替代方案,以避免在项目中的每个方法体中编写/复制一千次调用此特定方法所需的指令的噩梦。
是否存在一种方法在运行时实现这一点?。也许......编写一个helper方法,该方法将被调用一次,以通过Reflection检索所有声明的方法,并在它们的方法体中执行代码注入点?。不确定如何做到这一点,也不确定这是否可行;可能使用Reflection。按照this answer中的建议发出
这是否可以在不依赖第三方依赖项(如this question中建议的Postsharp)的情况下实现?对我来说,真正使用Postsharp不是一个可行的解决方案,因为我认为该解决方案将包括用自定义属性装饰项目中每个声明的方法。
我还发现了this other suggestion,但这基本上是我打算避免的:手动将代码更改添加到项目中的每个方法调用(在本例中是替换调用代码)。
我最后发现的一些建议不适用于我的场景,因为我需要在其他方法的方法体内部/从其他方法的方法体调用这个特定的方法,以便能够检索当前调用方成员的调试信息,包括其参数和值。

dbf7pr2w

dbf7pr2w1#

作为一个选项,RealProxy可能会有所帮助。Bruno Sonnino有一篇关于这个主题的不错的文章:Aspect-Oriented Programming : Aspect-Oriented Programming with the RealProxy Class.
你也可以使用Unity(DI框架)的AOP特性来实现这个目的,Dino Esposito有一篇关于这个主题的文章:Interceptors in Unity.
使用像Fody这样的编织工具也是另一种选择。
使用Reflection.Emit创建代理类也是一种选择。

示例-使用RealProxy的AOP

具有MethodExecuting和MethodExecuted并在方法之前和之后运行的LogAttribute

使用RealProxy,您可以为类创建一个代理,这样当您调用方法时,代理的Invoke方法将运行,并且您可以在那里运行任何逻辑,例如,您可以在实际方法调用之前或之后运行某些内容。
在这个例子中,我展示了如何创建一个包含两个方法OnMethodExecutingOnMethodExecutedMethodFilterAttribute,然后如果你用一个从这个属性派生的属性来修饰你的方法,那么这些方法将在原始方法执行之前和之后运行。
查看代码,您会发现不一定需要属性,属性只是作为一个扩展点。
此示例中代码的用法如下所示:

var calc = CalculatorFactory.GetInstance();
var a = calc.Add(1, 2);
var b = calc.Subtract(1, 2);

其产生输出:

Add executing.
Add executed.
Subtract executing.
Subtract executed.
    • 使用语句**

这是一个可以用于方法的属性。它有OnMethodExecuting和OnMethodExecuted方法,当你获得类的代理并运行这些方法时,这两个过滤器方法将在由该属性修饰的方法之前和之后执行:

using System;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Linq;

[AttributeUsage(AttributeTargets.Method)]
public class MethodFilterAttribute : Attribute
{
    public int Order { get; set; }
    public virtual void OnMethodExecuting(
        MethodInfo methodInfo, object[] args) { }
    public virtual void OnMethodExecuted(
        MethodInfo methodInfo, object[] args, object result) { }
}
    • 日志属性**

MethodFilterAttribute的实现,在方法执行前后执行日志记录:

public class LogAttribute : MethodFilterAttribute
{
    override public void OnMethodExecuting(
        MethodInfo methodInfo, object[] args)
    {
        Console.WriteLine($"{methodInfo.Name} executing.");
    }
    override public void OnMethodExecuted(
        MethodInfo methodInfo, object[] args, object result)
    {
        Console.WriteLine($"{methodInfo.Name} executed.");
    }
}
    • 动态代理类**

创建对象的代理,如果运行对象的方法,并且该方法使用方法筛选器属性进行修饰,则OnActionExecuting和OnActionExecuted将运行。
查看代码,您会发现不一定需要属性,属性只是作为一个扩展点。

public class DynamicProxy<T> : RealProxy
{
    private readonly T original;
    public DynamicProxy(T original)
      : base(typeof(T))
    {
        this.original = original;
    }
    public override IMessage Invoke(IMessage msg)
    {
        var methodCall = msg as IMethodCallMessage;
        var methodInfo = methodCall.MethodBase as MethodInfo;
        try
        {
            var filters = methodInfo.GetCustomAttributes<MethodFilterAttribute>();
            if (filters.Any())
            {
                filters.OrderBy(x => x.Order).ToList()
                 .ForEach(f => f.OnMethodExecuting(methodInfo, methodCall.InArgs));
            }
            var result = methodInfo.Invoke(original, methodCall.InArgs);
            if (filters.Any())
            {
                filters.OrderBy(x => x.Order).ToList()
                 .ForEach(f => f.OnMethodExecuted(methodInfo, methodCall.InArgs, result));
            }
            return new ReturnMessage(result, null, 0,
              methodCall.LogicalCallContext, methodCall);
        }
        catch (Exception e)
        {
            return new ReturnMessage(e, methodCall);
        }
    }
}
    • ICalculator接口和计算器类**

Calculator的方法用Log属性修饰,这意味着在这些方法执行之前和之后,日志都会运行。
请注意:在代理的实现中,我们使用接口来寻找属性。同时,拥有接口也是必要的。

public interface ICalculator
{
    [Log]
    int Add(int x, int y);
    [Log]
    int Subtract(int x, int y);
}

public class Calculator : ICalculator
{
    public int Add(int x, int y)
    {
        return x + y;
    }
    public int Subtract(int x, int y)
    {
        return x - y;
    }
}
    • 计算器工厂**

返回ICalculator代理示例的工厂:

public class CalculatorFactory
{
    public static ICalculator GetInstance()
    {
        var original = new Calculator();
        return new DynamicProxy<ICalculator>(original)
            .GetTransparentProxy() as ICalculator;
    }
}
    • 用法**

使用factory和run方法获取接口的代理示例:

var calc = CalculatorFactory.GetInstance();
var a = calc.Add(1, 2);
var b = calc.Subtract(1, 2);

其产生输出:

Add executing.
Add executed.
Subtract executing.
Subtract executed.

相关问题