C# 截获方法和注入方法调用

本文关键字:方法 注入 调用 | 更新日期: 2023-09-27 18:15:02

我所拥有的是:

class ABC
{
    public void MethodA()
    {
        Console.WriteLine("In method A");
    }
    public void MethodB()
    {
        Console.WriteLine("In method B");
    }
    public void MethodC()
    {
        Console.WriteLine("In method C");
    }
}
class PQR
{
    public void MethodP()
    {
        Console.WriteLine("In method P");
    }
    public void MethodQ()
    {
        Console.WriteLine("In method Q");
    }
    public void MethodR()
    {
        Console.WriteLine("In method R");
    }
}

我想要实现的是:

调用(或者可能使用任何 DI 框架注入(

MethodP()MethodA()MethodQ()MethodB()MethodR()MethodC()

但不扩展Class PQR Class ABC反之亦然。或者不修改Class ABC,我可以修改Class PQR。我确实检查了一些现有的 DI 框架,如 PrismAutofacUnity但要使用它们,我必须修改Class ABC(添加一些属性、扩展到一些接口等(,这是我不想做的。

我怎样才能做到这一点?

更新 1:ABC 和类 PQR 没有任何共同的超类/接口。

C# 截获方法和注入方法调用

这方面的一般设计模式是装饰器模式。您可以通过为ABC定义接口IABC来执行此操作:

interface IAbc {
    void MethodA();
    void MethodB();
    void MethodC();
}

现在,您可以为IABC定义一个装饰器,该装饰器能够在调用ABC之前"拦截"对ABC的调用和调用PQR

class AbcToPqrDecorator : IAbc
{
    private readonly PQR pqr;
    private readonly IAbc decorated;
    public AbcToPqrDecorator(PQR pqr, IAbc decorated) {
        this.pqr = pqr;
        this.decorated = decorated;
    }
    public void MethodA() {
        pqr.MethodP();
        decorated.MethodA();
    }
    public void MethodB() {
        pqr.MethodQ();
        decorated.MethodB();
    }
    public void MethodC() {
        pqr.MethodR();
        decorated.MethodC();
    }
}

您可以按如下方式创建对象图:

IAbc abc = new AbcToPqrDecorator(new PQR(), new ABC());

重要说明:如果您发现使用装饰器会导致大量开销(因为您需要定义许多具有相同行为但接口不同的装饰器实现(,这表明您违反了 SOLID 并缺少一个通用抽象。您的评论表明情况确实如此:

我有50+这样的课程,比如ABC。

作为如何设计系统的示例

,本文描述了如何设计系统的一部分,以便将MethodA()MethodB()MethodC()等操作包装在一个只需要定义一次的通用装饰器中变得微不足道。

如何处理这类事情的一种方法是Castle.DynamicProxy。有一些缺点 - 如果你不实现类而不是接口,则需要virtual方法才能使拦截工作:

//Install-Package Castle.DynamicProxy
public class Interceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.Out.WriteLine("Intercepting: " + invocation.Method.Name);
        invocation.Proceed();
    }
}
public class ABC
{
    public virtual void MethodA()
    {
        Console.WriteLine("In method A");
    }
    public void MethodB()
    {
        Console.WriteLine("In method B");
    }
}

用法:

var generator = new ProxyGenerator();
var abc = generator.CreateClassProxy<ABC>(new Interceptor());
// "Intercepting: MethodA"
// "In method A"
abc.MethodA();
// oops - not virtual method - no interception
// "In method B"
abc.MethodB();

PostSharp功能强大得多,AOP魔术发生在构建之后(因此性能更高(,不幸的是它不是免费的。

我正在研究NConcern .NET AOP框架,这是一个新的开源AOP框架,在运行时工作,没有任何工厂/代理模式和反射性能成本。它似乎能够处理您需要的东西。

请看一看。

public class MonkeyPatch : IAspect
{
    static public void Patch(MethodInfo oldMethod, MethodInfo newMethod)
    {
        //update monkey patch dictionary
        MonkeyPatch.m_Dictionary[oldMethod] = newMethod;
        //release previous monkey patch for target method.
        Aspect.Release<MonkeyPatch>(oldMethod);
        //weave monkey patch for target method.
        Aspect.Weave<MonkeyPatch>(oldMethod);
    }
    static private Dictionary<MethodInfo, MethodInfo> m_Dictionary = new Dictionary<MethodInfo, MethodInfo>();
    public IEnumerable<IAdvice> Advise(MethodInfo method)
    {
        if (MonkeyPatch.m_Dictionary.ContainsKey(_Method))
        {
            yield return Advice(MonkeyPatch.m_Dictionary[_Method]);
        }
    }
}

用法

static public void main(string[] args)
{
    //MonkeyPatch.Patch(typeof(ABC).GetMethod("A"), typeof(PQR).GetMethod("P"));
    MonkeyPatch.Patch(Metadata<ABC>.Method(_ABC => _ABC.A()), Metadata<PQR>.Method(_PQR=> _PQR.P()));
    //MonkeyPatch.Patch(typeof(ABC).GetMethod("B"), typeof(PQR).GetMethod("Q"));
    MonkeyPatch.Patch(Metadata<ABC>.Method(_ABC => _ABC.B()), Metadata<PQR>.Method(_PQR=> _PQR.Q()));
    //MonkeyPatch.Patch(typeof(ABC).GetMethod("C"), typeof(PQR).GetMethod("R"));
    MonkeyPatch.Patch(Metadata<ABC>.Method(_ABC => _ABC.C()), Metadata<PQR>.Method(_PQR=> _PQR.R())); 
}

我真的认为您的PQR类包含event而不是方法,因为您想在另一个方法中注入一个方法:

class PQR
{
    public event EventHandler MethodP;
    public event EventHandler MethodQ;
    public event EventHandler MethodR;
    public PQR()
    {
        MethodP += delegate { MethodP1(); };
        MethodQ += delegate { MethodQ1(); };
        MethodR += delegate { MethodR1(); };
    }
    private void MethodP1()
    {
        Console.WriteLine("In method P");
    }
    private void MethodQ1()
    {
        Console.WriteLine("In method Q");
    }
    private void MethodR1()
    {
        Console.WriteLine("In method R");
    }
}

然后,您可以简单地向事件添加方法:

var abc =  new ABC();
var pqr = new PQR();
pqr.MethodP += delegate { abc.MethodA(); };
pqr.MethodQ += delegate { abc.MethodB(); };
pqr.MethodR += delegate { abc.MethodC(); };

通过这种方式,您可以让您的ABC课程保持不变。