在Java中,可以使用AspectJ在执行方法之前和之后使用method annotations添加行为。由于C#属性似乎非常相似,我想知道是否有可能实现类似的行为。我正在查看几个教程和其他来源(1,2,3),但没有一个帮助过我。
我想也许我可以通过将代码插入到Attribute构造函数中并使其成为一次性来模仿行为,如下所示:
[AttributeUsage(AttributeTargets.Method)]
public class MyWritingAttribute : Attribute, IDisposable
{
public MyWritingAttribute()
{
Console.WriteLine("Attribute created");
}
public void Dispose()
{
Console.WriteLine("Attribute disposed");
}
}
但是,当使用这样的属性时,控制台中只显示 Hello world!:
class Program
{
static void Main(string[] args)
{
SayHelloWorld();
Console.ReadLine();
}
[MyWriting]
private static void SayHelloWorld()
{
Console.WriteLine("Hello World!");
}
}
我在想,可能在属性中无法访问Console,但即使用throw new Exception()
表达式替换它,也不会抛出任何异常。 EF的StringLengthAttribute
如何工作,但我的属性甚至没有被实例化?如何在装饰方法之前和之后运行属性?
答案 0 :(得分:3)
就像Java和AspectJ一样,你需要单独的AoP工具来在.NET中注入这样的代码。
PostSharp就是这样一个工具,可能是最知名的。我相信他们从版本5开始就支持.NET核心。
答案 1 :(得分:3)
您需要一些能够适当处理您的属性的框架。仅因为该属性存在并不意味着它将具有任何影响。
我写了一些简单的引擎来做到这一点。它将确定传递的action
上是否存在该属性,如果是,则获取反射的方法以执行它们。
class Engine
{
public void Execute(Action action)
{
var attr = action.Method.GetCustomAttributes(typeof(MyAttribute), true).First() as MyAttribute;
var method1 = action.Target.GetType().GetMethod(attr.PreAction);
var method2 = action.Target.GetType().GetMethod(attr.PostAction);
// now first invoke the pre-action method
method1.Invoke(null, null);
// the actual action
action();
// the post-action
method2.Invoke(null, null);
}
}
public class MyAttribute : Attribute
{
public string PreAction;
public string PostAction;
}
当然你需要一些零空格,例如如果方法不存在或不是静态的。
现在你必须用属性装饰你的动作:
class MyClass
{
[MyAttribute(PreAction = "Handler1", PostAction = "Handler2")]
public void DoSomething()
{
}
public static void Handler1()
{
Console.WriteLine("Pre");
}
public static void Handler2()
{
Console.WriteLine("Post");
}
}
最后,您可以在我们的引擎中执行该方法:
var engine = new Engine();
var m = new MayClass();
engine.Execute(m.DoSomething);
答案 2 :(得分:2)
该问题类似于Run a method before all methods of a class,因此相同的答案适用于两者。 使用https://github.com/Fody/Fody。许可模式基于自愿捐款,这使其成为PostSharp的更好选择,这对我来说有点贵。
[module: Interceptor]
namespace GenericLogging
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Assembly | AttributeTargets.Module)]
public class InterceptorAttribute : Attribute, IMethodDecorator
{
// instance, method and args can be captured here and stored in attribute instance fields
// for future usage in OnEntry/OnExit/OnException
public void Init(object instance, MethodBase method, object[] args)
{
Console.WriteLine(string.Format("Init: {0} [{1}]", method.DeclaringType.FullName + "." + method.Name, args.Length));
}
public void OnEntry()
{
Console.WriteLine("OnEntry");
}
public void OnExit()
{
Console.WriteLine("OnExit");
}
public void OnException(Exception exception)
{
Console.WriteLine(string.Format("OnException: {0}: {1}", exception.GetType(), exception.Message));
}
}
public class Sample
{
[Interceptor]
public void Method(int test)
{
Console.WriteLine("Your Code");
}
}
}
[TestMethod]
public void TestMethod2()
{
Sample t = new Sample();
t.Method(1);
}
答案 3 :(得分:1)
这可以使用DynamicProxy来完成。这里有关于如何执行此操作的说明AOP in .Net Core using AutoFac and DynamicProxy。
存在一种内存缓存技术的实现,其中逻辑在调用方法之前执行。这可以扩展到检查是否存在这样的属性
var attribute = Attribute.GetCustomAttribute(invocation.MethodInvocationTarget, typeof(CachedAttribute)) as CachedAttribute;
if (attribute != null)
{
...
}
上面的代码可以在Interceptor实现中的Intercept
方法内。 CachedAttribute
将是您的属性。