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

时间:2022-09-02 12:09:34

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.

我完全不知道怎么做或者根本不可能,但System.Diagnostic.ConditionalAttribute可能在后台做类似的事情。我不确定。

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

编辑:我忘了提到,由于我的具体情况,性能不是一个问题。

7 个解决方案

#1


17  

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.

.NET Framework 4.x提供了几个触发操作的属性,例如:ExceptionFilterAttribute(处理异常),AuthorizeAttribute(处理授权)。两者都在System.Web.Http.Filters中定义。

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:

然后,在您的控制器类中,您将装饰应该使用您的授权的方法,如下所示:

[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 Framework。它在内部使用接口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执行此操作的示例。请注意,有一个Express版本,即使是商业项目,您也可以免费使用。

#3


9  

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

您需要某种面向方面的框架。 PostSharp将会这样做,Windsor也是如此。

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

基本上,他们将您的对象子类化并覆盖此方法...

then it becomes:

然后它变成:

//proxy
public 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.

你可以看一下穷人的解决方案:看看装饰模式。

#1


17  

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.

.NET Framework 4.x提供了几个触发操作的属性,例如:ExceptionFilterAttribute(处理异常),AuthorizeAttribute(处理授权)。两者都在System.Web.Http.Filters中定义。

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:

然后,在您的控制器类中,您将装饰应该使用您的授权的方法,如下所示:

[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 Framework。它在内部使用接口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执行此操作的示例。请注意,有一个Express版本,即使是商业项目,您也可以免费使用。

#3


9  

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

您需要某种面向方面的框架。 PostSharp将会这样做,Windsor也是如此。

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

基本上,他们将您的对象子类化并覆盖此方法...

then it becomes:

然后它变成:

//proxy
public 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.

你可以看一下穷人的解决方案:看看装饰模式。