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