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

时间:2016-10-25 13:56:18

标签: c# dependency-injection aop

我拥有的是:

MethodP()

我想要达到的目标是:

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

MethodA()中的

MethodQ()MethodB()中的MethodR()MethodC()中的Class PQR

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

我怎样才能做到这一点?

更新1: 班级PQR和班级<h2>Header 1</h2> <p>Some text</p> ... <p>Some text</p> <h3>Sub-header 1</h3> <p>Some text</p> ... <p>Some text</p> <h2>Header 2</h2> 没有任何共同的超类/接口。

4 个答案:

答案 0 :(得分:2)

这个的一般设计模式是Decorator Pattern。您可以通过为IABC

定义接口ABC来执行此操作
interface IAbc {
    void MethodA();
    void MethodB();
    void MethodC();
}

现在,您可以为IABC定义一个能够拦截&#39;的装饰器。在致电ABC之前拨打PQR并致电ABC

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这样的课程。

作为如何设计系统的示例,this article描述了如何设计系统的一部分,以便包装诸如MethodA()MethodB()之类的操作变得微不足道和MethodC()只有一个通用装饰器,只需要定义一次。

答案 1 :(得分:0)

我真的看到你的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课程不受影响。

答案 2 :(得分:0)

如何处理这类东西的一种方法是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魔法发生在构建之后(因此性能更高),不幸的是它不是免费的。

答案 3 :(得分:0)

我正在研究NConcern .NET AOP Framework,这是一个新的开源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())); 
}