簡體   English   中英

在每個其他方法調用上動態調用特定方法

[英]Dynamically call a specific method on every other method call

我在 .NET 4.8 上,在 Windows Forms 下。我在項目中的各個類中有特定數量的聲明方法。 而且我希望這些類中的每個方法在每個方法執行的一開始就調用另一個特定的方法。 我的意思是,如果我在每個方法體內的第一行手動添加所需的代碼以調用其他特定方法。

我為什么要這樣做?:我寫了一個特定的方法來打印我有興趣知道的調試信息。 此方法包括有關調用方成員的信息,但它不以任何方式限於此類信息。

然后,當我在調試模式下開發任何應用程序時,我想在我的項目中的每個其他方法調用中自動調用此方法(如果可能的話,也在屬性 getter/setter 內部),我很好奇是否存在一種替代方案,可以替代在項目中的每個方法主體中編寫/復制調用此特定方法所需的指令一千次的噩夢。

存在一種在運行時實現此目的的方法嗎? 也許......編寫一個將被調用一次的輔助方法以通過反射檢索所有聲明的方法並在它們的方法體中做代碼注入點?。 不確定該怎么做,也不確定這是否可行; 也許按照這個答案中的建議使用Reflection.Emit

這是否可以在不依賴第三方依賴項(如本問題中建議的Postsharp )的情況下實現? 真正使用Postsharp對我來說不是一個可行的解決方案,因為我認為解決方案將包括用自定義屬性裝飾項目中的每個聲明方法。

我還發現了其他建議,但這基本上是我打算避免的:手動向項目中的每個方法調用添加代碼更改(在本例中是替換調用代碼)。

我最后發現的一些建議不適用於我的場景,因為我需要在其他方法的方法體中調用此特定方法,以便能夠檢索當前調用者成員的調試信息,包括其參數和值。

作為一個選項, RealProxy可能會有所幫助。 Bruno Sonnino 有一篇關於此主題的精彩文章: 面向方面的編程:使用 RealProxy 進行面向方面的編程 Class

您也可以為此目的使用Unity (DI 框架)的 AOP 功能。 Dino Esposito 有一篇關於此主題的精彩文章: Unity 中的攔截器

使用像Fody這樣的編織工具也是另一種選擇。

使用Reflection.Emit創建代理類可能是另一種選擇。

示例 - 使用 RealProxy 的 AOP

具有 MethodExecuting 和 MethodExecuted 並在方法之前和之后運行的 LogAttribute

使用 RealProxy,你可以為你的 class 創建一個代理,這樣當你調用一個方法時,代理的Invoke方法就會運行,你可以在那里運行任何邏輯,例如你可以在實際方法調用之前或之后運行一些東西。

在此示例中,我展示了如何創建具有兩個方法MethodFilterAttributeOnMethodExecutingOnMethodExecuted ,然后如果您使用從該屬性派生的屬性裝飾您的方法,那么這些方法將在執行原始方法之前和之后運行。

查看代碼,您會發現您不一定需要屬性,屬性只是作為擴展點存在。

這個例子中代碼的用法是這樣的:

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

產生 output:

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

使用語句

這是一個可用於方法的屬性。 它有 OnMethodExecuting 和 OnMethodExecuted 方法,當你得到你的 class 的代理,並運行這些方法時,這兩個過濾器方法將在這個屬性修飾的方法之前和之后執行:

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.");
    }
}

動態代理 class

創建你的 object 的代理,如果你運行 object 的方法,如果方法用方法過濾器屬性修飾,那么 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界面和計算器 class

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;
    }
}

用法

使用工廠和運行方法獲取接口的代理實例:

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

產生 output:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM