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 框架,如 Prism
、Autofac
、Unity
但要使用它们,我必须修改Class ABC
(添加一些属性、扩展到一些接口等(,这是我不想做的。
我怎样才能做到这一点?
更新 1:类 ABC
和类 PQR
没有任何共同的超类/接口。
这方面的一般设计模式是装饰器模式。您可以通过为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
课程保持不变。