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

C#拦截方法和注入方法调用,c#,dependency-injection,aop,C#,Dependency Injection,Aop,我拥有的是: 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 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");
    }
}
我想要实现的是:

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框架注入)

MethodA()
中的
MethodP()
MethodQ()
MethodB()中,
MethodR()
MethodC()

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

我怎样才能做到这一点

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

这方面的一般设计模式是。您可以通过为
ABC
定义接口
IABC
来实现这一点:

interface IAbc {
    void MethodA();
    void MethodB();
    void MethodC();
}
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
之前“拦截”对
ABC
的调用和调用
PQR

interface IAbc {
    void MethodA();
    void MethodB();
    void MethodC();
}
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());
重要提示:如果您发现使用decorator会导致大量开销(因为您需要定义许多具有相同行为但针对不同接口的decorator实现),这表明您违反了SOLID,缺少了一个公共抽象。你的评论表明情况确实如此:

我有50多个像ABC这样的课程


作为如何设计系统的一个示例,描述了如何设计系统的一部分,使像
MethodA()
MethodB()
MethodC()这样的操作变得简单
使用一个只需定义一次的泛型装饰器。

我确实认为您的
PQR
类包含一个
事件而不是方法,因为您希望在另一个方法中插入一个方法:

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类不受影响。

一种处理这种东西的方法是Castle.DynamicProxy。有缺点-如果您不实现类而不是接口,那么方法需要是
虚拟的
,以便拦截工作:

//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();
var生成器=新的ProxyGenerator();
var abc=generator.CreateClassProxy(新的拦截器());
//“拦截:MethodA”
//“在方法A中”
abc.MethodA();
//oops-非虚拟方法-无拦截
//“在方法B中”
abc.MethodB();
PostSharp功能更强大,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]);
        }
    }
}
公共类MonkeyPatch:IAspect
{
静态公共无效修补程序(MethodInfo oldMethod、MethodInfo newMethod)
{
//更新猴子补丁字典
MonkeyPatch.m_Dictionary[oldMethod]=newMethod;
//为目标方法发布以前的monkey补丁。
方面。发布(旧方法);
//为目标方法编织猴子补丁。
织法(旧法);
}
静态专用字典m_Dictionary=新字典();
公共IEnumerable建议(MethodInfo方法)
{
if(MonkeyPatch.m_Dictionary.ContainsKey(_方法))
{
收益回报建议(MonkeyPatch.m_字典[_方法]);
}
}
}
用法

static public void main(字符串[]args)
{
//补丁(typeof(ABC.GetMethod(“A”)、typeof(PQR.GetMethod(“P”));
补丁(Metadata.Method(_ABC=>_ABC.A()),Metadata.Method(_PQR=>_PQR.P());
//补丁(typeof(ABC.GetMethod(“B”)、typeof(PQR.GetMethod(“Q”));
补丁(Metadata.Method(_ABC=>_ABC.B()),Metadata.Method(_PQR=>_PQR.Q());
//补丁(typeof(ABC.GetMethod(“C”)、typeof(PQR.GetMethod(“R”));
补丁(Metadata.Method(_ABC=>_ABC.C()),Metadata.Method(_PQR=>_PQR.R());
}

如果使用
Castle Windsor
使用Do
ABC
PQR
实现或扩展相同的接口/基类?如果是这样,您应该将
ABC
封装在
PQR
内部,并注入
PQR
的实例,而不是
ABC
。您不是真的想要DI,而是想要类似PostSharp的东西。@GSerg请选择一个!我会把它敲碎…。。@rory.ap我想答案可能不够好。也许是一个典型问题/答案的候选人。@meJustAndrew-woops,这是一个打字错误。修正了。太好了,现在是一个有效的答案!但是你刚刚修改了规则(修改了类
ABC
)。@Steven,你能详细说明一下实体违规部分吗?具体来说,关于你提到的装饰师。你是说不同接口的类似装饰器吗?@YacoubMassad因为ABC实现了3种方法,所以不可能所有的消费者都使用这3种方法。如果没有,ABC违反了接口隔离原则。@YacoubMassad如果您需要具有相同行为的多个装饰器,但对于不同的接口,很可能缺少一些一般抽象。但是这样,MethodA将在MethodB内部调用。我希望它以另一种方式发生。nope@Yogesh
MethodA
MethodB
仍然是完全分开的。告诉我为什么你认为它们彼此相关?哦,对不起,我是说MethodP()而不是MethodB()。@Yogesh好的,我更新了我的答案,以便告诉你
MethodP()
MethodA()
仍然是分开的。您不能在中调用
MethodP