我希望能够在方法上放置一个属性,并让该方法检查方法参数和返回值。伪代码。
[AttributeUsage(AttributeTargets.Method)]
class MyAttribute: Attribute {
public void MethodEnter(MethodInfo info) {
foreach (var param in info.MethodParameters {
Console.WriteLine(param.ToString());
}
}
public void MethodLeave(MethodInfo info) {
Console.WriteLine(info.ReturnValue);
}
}
public class MyClass {
[MyAttribute]
public SomeType foo(Order order, List<OrderLine> orderLines) {
...
}
}
这可能吗?
基本上,我希望能够记录所有内容并离开该功能。我之前使用PostSharp完成了这项工作,但使用它进行日志记录似乎有些过分。
答案 0 :(得分:1)
您基本上寻找AOP(面向方面编程)解决方案。 PostSharp在编译后在二进制文件中使用IL注入,在我看来,这是我使用过的最糟糕的框架。
我建议你装饰你的课程,而不是寻找一些非常复杂的解决方案。例如:
public MyClass : IMyClass
{
public object[] MyMethod(object[] args){...}
}
public LoggerDecoratedMyClass : IMyClass
{
private readonly IMyClass _inner;
private readonly ILogger _logger;
public LoggerDecoratedMyClass(IMyClass inner, ILogger logger)
{
_inner = inner;
_logger = logger;
}
public object[] MyMethod(object[] args)
{
try
{
var result = _inner.MyMethod(args);
_logger.LogSuccess(...);
return result;
}
catch (Exception ex)
{
_logger.LogError(..., ex);
throw;
}
}
}
这看起来比属性绑定好很多,并且为您提供了控制 Aspect dependecies 的能力。此外,它强制您编写面向接口的代码。
<强>更新强>
另外,我经常使用范围记录:
internal struct ScopeLogger : IDisposable
{
private readonly string _name;
public ScopeLogger(ILogger logger, string scopeName, object[] args)
{
_name = scopeName;
_logger = logger;
_logger.LogInfo("Begin {name}: {args}", _name, args);
}
public void Dispose()
{
_logger.LogInfo("End {name}",_name);
}
}
public static IDisposable LogScope(this ILogger logger, string name, params object[] args)
{
return new ScopeLogger(logger, name, args);
}
只需像这样使用它:
public LoggerDecoratedMyClass : IMyClass
{
private readonly IMyClass _inner;
private readonly ILogger _logger;
public LoggerDecoratedMyClass(IMyClass inner, ILogger logger)
{
_inner = inner;
_logger = logger;
}
public object[] MyMethod(object[] args)
{
using(_logger.LogScope(nameof(MyMethod), args))
{
return _inner.MyMethod(args);
}
}
}
这种装饰器看起来好多又短。
答案 1 :(得分:1)
您可以通过使您的类派生自 ContextBoundObject 以及您的属性来自 ContextAttribute 来创建方面。通过阅读this文章,我制作了以下满足您要求的样本:
首先,我们的Foo类派生自ContextBoundObject,使用我们的自定义类属性
进行修饰 [Inpectable]
internal class Foo : ContextBoundObject
{
[InspectableProperty]
public int DoSomething(int a)
{
Console.WriteLine("I am doing something");
a += 1;
return a;
}
public void IamNotLogged()
{
Console.WriteLine("Lol");
}
[InspectableProperty]
public string IamLoggedToo()
{
var msg = "Lol too";
Console.WriteLine(msg);
return msg;
}
}
现在是Inspectable属性
[AttributeUsage(AttributeTargets.Class)]
public class Inpectable : ContextAttribute
{
public Inpectable() : base("Inspectable")
{
}
public override void GetPropertiesForNewContext(IConstructionCallMessage ccm)
{
ccm.ContextProperties.Add(new InspectorProperty());
}
}
InspectablePropertyAttribute仅用于识别应记录的那些方法:
[AttributeUsage(AttributeTargets.Method)]
public class InspectableProperty : Attribute
{
}
InspectorProperty,它将捕获实例的上下文并拦截将它们传递给我们方面的消息
public class InspectorProperty : IContextProperty,
IContributeObjectSink
{
public bool IsNewContextOK(Context newCtx) => true;
public void Freeze(Context newContext)
{
}
public string Name { get; } = "LOL";
public IMessageSink GetObjectSink(MarshalByRefObject o,IMessageSink next) => new InspectorAspect(next);
}
魔法的作用,我们的InspectorAspect的实现:
internal class InspectorAspect : IMessageSink
{
internal InspectorAspect(IMessageSink next)
{
NextSink = next;
}
public IMessageSink NextSink { get; }
public IMessage SyncProcessMessage(IMessage msg)
{
if (!(msg is IMethodMessage)) return NextSink.SyncProcessMessage(msg);
var call = (IMethodMessage) msg;
var type = Type.GetType(call.TypeName);
if (type == null) return NextSink.SyncProcessMessage(msg);
var methodInfo = type.GetMethod(call.MethodName);
if (!Attribute.IsDefined(methodInfo, typeof (InspectableProperty)))
return NextSink.SyncProcessMessage(msg);
Console.WriteLine($"Entering method: {call.MethodName}. Args being:");
foreach (var arg in call.Args)
Console.WriteLine(arg);
var returnMethod = NextSink.SyncProcessMessage(msg) as IMethodReturnMessage;
Console.WriteLine($"Method {call.MethodName} returned: {returnMethod?.ReturnValue}");
Console.WriteLine();
return returnMethod;
}
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
throw new InvalidOperationException();
}
}
在 SyncProcessMessage 中,theInspectorAspect接收有关上下文(Foo)的所有消息,因此我们只过滤那些属于 IMethodMessage 的实例,然后只过滤属于装饰方法的那些使用 InspectableProperty 属性。
这可能不是一个随时可用的解决方案,但我认为这会让您走上正确的研究途径,获取更多信息。
最后测试:
private static void Main(string[] args)
{
var foo = new Foo();
foo.DoSomething(1);
foo.IamNotLogged();
foo.IamLoggedToo();
Console.ReadLine();
}
输出:
编辑&gt;&GT;&GT;
这些是所需的命名空间: