c#:当方法被调用时,如何在触发事件的方法上创建属性?

时间:2022-09-02 12:14:58

Is there a way in C# or .NET in general to create an attribute on a method which triggers an event when the method is invoked? Ideally, I would be able to run custom actions before and after the invocation of the method.

c#或。net中是否有一种方法在方法上创建一个属性,当方法被调用时触发事件?理想情况下,我将能够在方法调用之前和之后运行自定义操作。

I mean something like this:

我的意思是这样的:

[TriggersMyCustomAction()]public void DoSomeStuff(){}

I am totally clueless how to do it or if it possible at all, but System.Diagnostic.ConditionalAttribute might do a similar thing in the background. I am not sure though.

我完全不知道该怎么做,或者是否有可能,但是系统。诊断。ConditionalAttribute可能在后台做类似的事情。但我不确定。

EDIT: I forgot to mention that due to the circumstances of my specific case, performance is not really an issue.

编辑:我忘了说,由于我的具体情况,性能并不是真正的问题。

7 个解决方案

#1


16  

The only way I know how to do this is with PostSharp. It post-processes your IL and can do things like what you asked for.

我知道怎么做的唯一方法就是使用PostSharp。它会处理你的IL,可以做你想要的事情。

#2


18  

This concept is used in MVC web applications.

这个概念用于MVC web应用程序。

The .NET Framework 4.x provides several attributes which trigger actions, e.g.: ExceptionFilterAttribute (handling exceptions), AuthorizeAttribute (handling authorization). Both are defined in System.Web.Http.Filters.

4 . net框架。x提供几个触发操作的属性,例如:ExceptionFilterAttribute(处理异常)、AuthorizeAttribute(处理授权)。它们都在system . web . http . filter中定义。

You could for instance define your own authorization attribute as follows:

例如,您可以定义自己的授权属性如下:

public class myAuthorizationAttribute : AuthorizeAttribute{    protected override bool IsAuthorized(HttpActionContext actionContext)    {        // do any stuff here        // it will be invoked when the decorated method is called        if (CheckAuthorization(actionContext))            return true; // authorized        else           return false; // not authorized    }}

Then, in your controller class you decorate the methods which are supposed to use your authorization as follows:

然后,在你的controller类中,你装饰了应该使用你的授权的方法如下:

[myAuthorization]public HttpResponseMessage Post(string id){    // ... your code goes here    response = new HttpResponseMessage(HttpStatusCode.OK); // return OK status    return response;}

Whenever the Post method is invoked, it will call the IsAuthorized method inside the myAuthorization Attribute before the code inside the Post method is executed.

无论何时调用Post方法,它都将在执行Post方法内部的代码之前调用myAuthorization属性中的IsAuthorized方法。

If you return false in the IsAuthorized method, you signal that authorization is not granted and the execution of the method Post aborts.

如果在IsAuthorized方法中返回false,则表示未授予授权,方法Post的执行将中止。


To understand how this works, let's look into a different example: The ExceptionFilter, which allows filtering exceptions by using attributes, the usage is similar as shown above for the AuthorizeAttribute (you can find a more detailed description about its usage here).

为了理解这是如何工作的,让我们看一个不同的示例:ExceptionFilter,它允许通过使用属性来过滤异常,它的用法与上面显示的AuthorizeAttribute类似(您可以在这里找到关于其用法的更详细描述)。

To use it, derive the DivideByZeroExceptionFilter class from the ExceptionFilterAttribute as shown here, and override the method OnException:

要使用它,从ExceptionFilterAttribute派生DivideByZeroExceptionFilter类,并覆盖方法OnException:

public class DivideByZeroExceptionFilter : ExceptionFilterAttribute{    public override void OnException(HttpActionExecutedContext actionExecutedContext)    {        if (actionExecutedContext.Exception is DivideByZeroException)        {            actionExecutedContext.Response = new HttpResponseMessage() {                 Content = new StringContent("An error occured within the application.",                                System.Text.Encoding.UTF8, "text/plain"),                 StatusCode = System.Net.HttpStatusCode.InternalServerError                };        }    }}

Then use the following demo code to trigger it:

然后使用以下演示代码触发:

[DivideByZeroExceptionFilter]public void Delete(int id){    // causes the DivideByZeroExceptionFilter attribute to be triggered:    throw new DivideByZeroException(); }

Now that we know how it is used, we're mainly interested in the implementation. The following code is from the .NET Framework. It uses the interface IExceptionFilter internally as a contract:

现在我们知道了如何使用它,我们主要对实现感兴趣。下面的代码来自。net框架。它内部使用接口IExceptionFilter作为合同:

namespace System.Web.Http.Filters{    public interface IExceptionFilter : IFilter    {        // Executes an asynchronous exception filter.        // Returns: An asynchronous exception filter.        Task ExecuteExceptionFilterAsync(                    HttpActionExecutedContext actionExecutedContext,                     CancellationToken cancellationToken);    }}

The ExceptionFilterAttribute itself is defined as follows:

ExceptionFilterAttribute本身的定义如下:

namespace System.Web.Http.Filters{    // Represents the attributes for the exception filter.    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,             Inherited = true, AllowMultiple = true)]    public abstract class ExceptionFilterAttribute : FilterAttribute,             IExceptionFilter, IFilter    {        // Raises the exception event.        // actionExecutedContext: The context for the action.</param>        public virtual void OnException(            HttpActionExecutedContext actionExecutedContext)        {        }        // Asynchronously executes the exception filter.        // Returns: The result of the execution.        Task IExceptionFilter.ExecuteExceptionFilterAsync(            HttpActionExecutedContext actionExecutedContext,             CancellationToken cancellationToken)        {            if (actionExecutedContext == null)            {                throw Error.ArgumentNull("actionExecutedContext");            }            this.OnException(actionExecutedContext);            return TaskHelpers.Completed();        }    }}

Inside ExecuteExceptionFilterAsync, the method OnException is called. Because you have overridden it as shown earlier, the error can now be handled by your own code.

在ExecuteExceptionFilterAsync内部,调用OnException方法。因为您已经像前面显示的那样重写了它,所以错误现在可以由您自己的代码处理。


There is also a commercial product available as mentioned in OwenP's answer, PostSharp, which allows you to do that easily. Here is an example how you can do that with PostSharp. Note that there is an Express edition available which you can use for free even for commercial projects.

正如OwenP的答案PostSharp所提到的,还有一种商业产品可以让你轻松地做到这一点。这里有一个使用PostSharp的例子。请注意,有一个可用的快速版本,即使是商业项目,你也可以免费使用。

#3


9  

You need some sort of Aspect oriented framework. PostSharp will do it, as will Windsor.

你需要某种面向方面的框架。PostSharp也会这么做,温莎也会这么做。

Basically, they subclass your object and override this method...

基本上,他们对你的对象进行子类化并覆盖这个方法……

then it becomes:

然后它就变成了:

//proxypublic override void DoSomeStuff(){     if(MethodHasTriggerAttribute)        Trigger();     _innerClass.DoSomeStuff();}

of course all this is hidden to you. All you have to do is ask Windsor for the type, and it will do the proxying for you. The attribute becomes a (custom) facility I think in Windsor.

当然,这一切对你来说都是隐藏的。您所要做的就是向Windsor请求类型,它将为您执行代理。我认为在温莎,属性变成了(自定义的)工具。

#4


3  

You can use ContextBoundObject and IMessageSink. See http://msdn.microsoft.com/nb-no/magazine/cc301356(en-us).aspx

您可以使用ContextBoundObject和IMessageSink。见http://msdn.microsoft.com/nb-no/magazine/cc301356(en - us). aspx

Be warned that this approach has a severe performance impact compared with a direct method call.

请注意,与直接的方法调用相比,这种方法对性能有严重的影响。

#5


0  

I don't think there is a way to do it with just an attribute, but using proxy classes and reflection you could have a class that knows to intercept instantiations of the classes in which you have attributed methods.

我不认为有一种方法可以只使用一个属性,但是使用代理类和反射,您可以有一个类,它知道如何拦截具有属性方法的类的实例化。

Then the proxy class can trigger an event whenever the attributed methods are called.

然后,无论何时调用带属性的方法,代理类都可以触发事件。

#6


0  

An attribute gives information, they are metadata. I don't know of a way to do this offhand, someone might.

属性提供信息,它们是元数据。我不知道有什么方法可以立刻做到,也许有人会这么做。

You could look at partial methods in .NET which allow you to do some lightweight event handling. You provide the hooks and let someone else handle the implementation. If the method isn't implemented the compiler just ignores it.

您可以在. net中查看部分方法,这些方法允许您进行一些轻量级的事件处理。您提供了钩子并让其他人来处理实现。如果方法没有实现,编译器就会忽略它。

http://msdn.microsoft.com/en-us/library/wa80x488.aspx

http://msdn.microsoft.com/en-us/library/wa80x488.aspx

#7


0  

You might take a look at the poor man's solution: see the decorator pattern.

您可以看看这个可怜人的解决方案:查看decorator模式。

#1


16  

The only way I know how to do this is with PostSharp. It post-processes your IL and can do things like what you asked for.

我知道怎么做的唯一方法就是使用PostSharp。它会处理你的IL,可以做你想要的事情。

#2


18  

This concept is used in MVC web applications.

这个概念用于MVC web应用程序。

The .NET Framework 4.x provides several attributes which trigger actions, e.g.: ExceptionFilterAttribute (handling exceptions), AuthorizeAttribute (handling authorization). Both are defined in System.Web.Http.Filters.

4 . net框架。x提供几个触发操作的属性,例如:ExceptionFilterAttribute(处理异常)、AuthorizeAttribute(处理授权)。它们都在system . web . http . filter中定义。

You could for instance define your own authorization attribute as follows:

例如,您可以定义自己的授权属性如下:

public class myAuthorizationAttribute : AuthorizeAttribute{    protected override bool IsAuthorized(HttpActionContext actionContext)    {        // do any stuff here        // it will be invoked when the decorated method is called        if (CheckAuthorization(actionContext))            return true; // authorized        else           return false; // not authorized    }}

Then, in your controller class you decorate the methods which are supposed to use your authorization as follows:

然后,在你的controller类中,你装饰了应该使用你的授权的方法如下:

[myAuthorization]public HttpResponseMessage Post(string id){    // ... your code goes here    response = new HttpResponseMessage(HttpStatusCode.OK); // return OK status    return response;}

Whenever the Post method is invoked, it will call the IsAuthorized method inside the myAuthorization Attribute before the code inside the Post method is executed.

无论何时调用Post方法,它都将在执行Post方法内部的代码之前调用myAuthorization属性中的IsAuthorized方法。

If you return false in the IsAuthorized method, you signal that authorization is not granted and the execution of the method Post aborts.

如果在IsAuthorized方法中返回false,则表示未授予授权,方法Post的执行将中止。


To understand how this works, let's look into a different example: The ExceptionFilter, which allows filtering exceptions by using attributes, the usage is similar as shown above for the AuthorizeAttribute (you can find a more detailed description about its usage here).

为了理解这是如何工作的,让我们看一个不同的示例:ExceptionFilter,它允许通过使用属性来过滤异常,它的用法与上面显示的AuthorizeAttribute类似(您可以在这里找到关于其用法的更详细描述)。

To use it, derive the DivideByZeroExceptionFilter class from the ExceptionFilterAttribute as shown here, and override the method OnException:

要使用它,从ExceptionFilterAttribute派生DivideByZeroExceptionFilter类,并覆盖方法OnException:

public class DivideByZeroExceptionFilter : ExceptionFilterAttribute{    public override void OnException(HttpActionExecutedContext actionExecutedContext)    {        if (actionExecutedContext.Exception is DivideByZeroException)        {            actionExecutedContext.Response = new HttpResponseMessage() {                 Content = new StringContent("An error occured within the application.",                                System.Text.Encoding.UTF8, "text/plain"),                 StatusCode = System.Net.HttpStatusCode.InternalServerError                };        }    }}

Then use the following demo code to trigger it:

然后使用以下演示代码触发:

[DivideByZeroExceptionFilter]public void Delete(int id){    // causes the DivideByZeroExceptionFilter attribute to be triggered:    throw new DivideByZeroException(); }

Now that we know how it is used, we're mainly interested in the implementation. The following code is from the .NET Framework. It uses the interface IExceptionFilter internally as a contract:

现在我们知道了如何使用它,我们主要对实现感兴趣。下面的代码来自。net框架。它内部使用接口IExceptionFilter作为合同:

namespace System.Web.Http.Filters{    public interface IExceptionFilter : IFilter    {        // Executes an asynchronous exception filter.        // Returns: An asynchronous exception filter.        Task ExecuteExceptionFilterAsync(                    HttpActionExecutedContext actionExecutedContext,                     CancellationToken cancellationToken);    }}

The ExceptionFilterAttribute itself is defined as follows:

ExceptionFilterAttribute本身的定义如下:

namespace System.Web.Http.Filters{    // Represents the attributes for the exception filter.    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,             Inherited = true, AllowMultiple = true)]    public abstract class ExceptionFilterAttribute : FilterAttribute,             IExceptionFilter, IFilter    {        // Raises the exception event.        // actionExecutedContext: The context for the action.</param>        public virtual void OnException(            HttpActionExecutedContext actionExecutedContext)        {        }        // Asynchronously executes the exception filter.        // Returns: The result of the execution.        Task IExceptionFilter.ExecuteExceptionFilterAsync(            HttpActionExecutedContext actionExecutedContext,             CancellationToken cancellationToken)        {            if (actionExecutedContext == null)            {                throw Error.ArgumentNull("actionExecutedContext");            }            this.OnException(actionExecutedContext);            return TaskHelpers.Completed();        }    }}

Inside ExecuteExceptionFilterAsync, the method OnException is called. Because you have overridden it as shown earlier, the error can now be handled by your own code.

在ExecuteExceptionFilterAsync内部,调用OnException方法。因为您已经像前面显示的那样重写了它,所以错误现在可以由您自己的代码处理。


There is also a commercial product available as mentioned in OwenP's answer, PostSharp, which allows you to do that easily. Here is an example how you can do that with PostSharp. Note that there is an Express edition available which you can use for free even for commercial projects.

正如OwenP的答案PostSharp所提到的,还有一种商业产品可以让你轻松地做到这一点。这里有一个使用PostSharp的例子。请注意,有一个可用的快速版本,即使是商业项目,你也可以免费使用。

#3


9  

You need some sort of Aspect oriented framework. PostSharp will do it, as will Windsor.

你需要某种面向方面的框架。PostSharp也会这么做,温莎也会这么做。

Basically, they subclass your object and override this method...

基本上,他们对你的对象进行子类化并覆盖这个方法……

then it becomes:

然后它就变成了:

//proxypublic override void DoSomeStuff(){     if(MethodHasTriggerAttribute)        Trigger();     _innerClass.DoSomeStuff();}

of course all this is hidden to you. All you have to do is ask Windsor for the type, and it will do the proxying for you. The attribute becomes a (custom) facility I think in Windsor.

当然,这一切对你来说都是隐藏的。您所要做的就是向Windsor请求类型,它将为您执行代理。我认为在温莎,属性变成了(自定义的)工具。

#4


3  

You can use ContextBoundObject and IMessageSink. See http://msdn.microsoft.com/nb-no/magazine/cc301356(en-us).aspx

您可以使用ContextBoundObject和IMessageSink。见http://msdn.microsoft.com/nb-no/magazine/cc301356(en - us). aspx

Be warned that this approach has a severe performance impact compared with a direct method call.

请注意,与直接的方法调用相比,这种方法对性能有严重的影响。

#5


0  

I don't think there is a way to do it with just an attribute, but using proxy classes and reflection you could have a class that knows to intercept instantiations of the classes in which you have attributed methods.

我不认为有一种方法可以只使用一个属性,但是使用代理类和反射,您可以有一个类,它知道如何拦截具有属性方法的类的实例化。

Then the proxy class can trigger an event whenever the attributed methods are called.

然后,无论何时调用带属性的方法,代理类都可以触发事件。

#6


0  

An attribute gives information, they are metadata. I don't know of a way to do this offhand, someone might.

属性提供信息,它们是元数据。我不知道有什么方法可以立刻做到,也许有人会这么做。

You could look at partial methods in .NET which allow you to do some lightweight event handling. You provide the hooks and let someone else handle the implementation. If the method isn't implemented the compiler just ignores it.

您可以在. net中查看部分方法,这些方法允许您进行一些轻量级的事件处理。您提供了钩子并让其他人来处理实现。如果方法没有实现,编译器就会忽略它。

http://msdn.microsoft.com/en-us/library/wa80x488.aspx

http://msdn.microsoft.com/en-us/library/wa80x488.aspx

#7


0  

You might take a look at the poor man's solution: see the decorator pattern.

您可以看看这个可怜人的解决方案:查看decorator模式。