对于给定的类,我希望有跟踪功能,即我想记录每个方法调用(方法签名和实际参数值)和每个方法退出(只是方法签名).
我如何做到这一点假设:
我不想为C#使用任何第三方AOP库,
我不想将重复的代码添加到我想要跟踪的所有方法中,
我不想更改类的公共API - 类的用户应该能够以完全相同的方式调用所有方法.
为了使问题更具体,让我们假设有3个类:
public class Caller { public static void Call() { Traced traced = new Traced(); traced.Method1(); traced.Method2(); } } public class Traced { public void Method1(String name, Int32 value) { } public void Method2(Object object) { } } public class Logger { public static void LogStart(MethodInfo method, Object[] parameterValues); public static void LogEnd(MethodInfo method); }
如何调用Logger.LogStart和Logger.LogEnd每次调用方法1和方法2,而无需修改Caller.Call方法,没有明确地加入调用Traced.Method1和Traced.Method2?
编辑:如果我允许稍微更改Call方法,会有什么解决方案?
C#不是面向AOP的语言.它有一些AOP功能,你可以模仿其他一些,但用C#制作AOP是很痛苦的.
我想方设法做你想做的事情,我找不到简单的办法.
据我了解,这是你想要做的:
[Log()] public void Method1(String name, Int32 value);
为了做到这一点,你有两个主要选择
从MarshalByRefObject或ContextBoundObject继承您的类,并定义一个继承自IMessageSink的属性.这篇文章有一个很好的例子.你必须考虑使用MarshalByRefObject,性能会像地狱一样下降,我的意思是,我说的是性能损失了10倍,所以在尝试之前要仔细考虑.
另一种选择是直接注入代码.在运行时,意味着你将不得不使用反射"读取"每个类,获取其属性并注入适当的调用(为此我认为你不能使用Reflection.Emit方法,因为我认为Reflection.Emit不会不允许您在现有方法中插入新代码.在设计时,这将意味着创建一个CLR编译器的扩展,我真的不知道它是如何完成的.
最后一个选择是使用IoC框架.也许它不是完美的解决方案,因为大多数IoC框架通过定义允许方法挂钩的入口点来工作,但是,根据您想要实现的目标,这可能是一个公平的近似.
实现这一目标的最简单方法可能是使用PostSharp.它根据您应用于它的属性在方法中注入代码.它允许您完全按照自己的意愿行事.
另一种选择是使用分析API在方法中注入代码,但这确实是硬核.
如果你编写一个类 - 称之为Tracing - 实现IDisposable接口,你可以将所有方法体包装在一个
Using( Tracing tracing = new Tracing() ){ ... method body ...}
在Tracing类中,您可以分别在Tracing类中处理构造函数/ Dispose方法中跟踪的逻辑,以跟踪方法的进入和退出.这样:
public class Traced { public void Method1(String name, Int32 value) { using(Tracing tracer = new Tracing()) { [... method body ...] } } public void Method2(Object object) { using(Tracing tracer = new Tracing()) { [... method body ...] } } }
您可以使用诸如Castle Windsor之类的DI容器的拦截功能来实现它.实际上,可以以这样的方式配置容器,即每个具有由特定属性修饰的方法的类都将被截获.
关于第3点,OP要求提供没有AOP框架的解决方案.我在下面的回答中假设应该避免的是Aspect,JointPoint,PointCut等.根据CastleWindsor的拦截文档,没有一个是完成所要求的.
public class RequireInterception : IContributeComponentModelConstruction { public void ProcessModel(IKernel kernel, ComponentModel model) { if (HasAMethodDecoratedByLoggingAttribute(model.Implementation)) { model.Interceptors.Add(new InterceptorReference(typeof(ConsoleLoggingInterceptor))); model.Interceptors.Add(new InterceptorReference(typeof(NLogInterceptor))); } } private bool HasAMethodDecoratedByLoggingAttribute(Type implementation) { foreach (var memberInfo in implementation.GetMembers()) { var attribute = memberInfo.GetCustomAttributes(typeof(LogAttribute)).FirstOrDefault() as LogAttribute; if (attribute != null) { return true; } } return false; } }
container.Kernel.ComponentModelBuilder.AddContributor(new RequireInterception());
public class ConsoleLoggingInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { Console.Writeline("Log before executing"); invocation.Proceed(); Console.Writeline("Log after executing"); } }
public class Traced { [Log] public void Method1(String name, Int32 value) { } [Log] public void Method2(Object object) { } }
请注意,如果只需要拦截某个类的某些方法,则需要对该属性进行一些处理.默认情况下,将截获所有公共方法.
如果您想无限制地跟踪方法(无代码改编,无AOP框架,无重复代码),请告诉我,您需要一些魔术...
认真地说,我解决了它,以实现在运行时工作的AOP框架。
您可以在这里找到:NConcern .NET AOP框架
我决定创建此AOP框架来响应这种需求。这是一个非常轻量的简单库。您可以在主页上看到记录器的示例。
如果您不想使用第三方组装,则可以浏览代码源(开放源代码),并复制文件Aspect.Directory.cs和Aspect.Directory.Entry.cs以适应您的需要。这些类允许在运行时替换您的方法。我只想请您尊重许可证。
我希望您能找到所需的内容或说服您最终使用AOP框架。