C #: ¿Cómo crear un atributo en un método que desencadena un evento cuando se invoca?

¿Existe alguna forma en C # o .NET en general para crear un atributo en un método que desencadena un evento cuando se invoca el método? Idealmente, podría ejecutar acciones personalizadas antes y después de la invocación del método.

Quiero decir algo como esto:

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

No tengo ni idea de cómo hacerlo o si es posible, pero System.Diagnostic.ConditionalAttribute podría hacer algo similar en segundo plano. No estoy seguro sin embargo.

EDITAR : Olvidé mencionar que debido a las circunstancias de mi caso específico, el rendimiento no es realmente un problema.

La única forma en que sé cómo hacerlo es con PostSharp . Post-procesa tu IL y puede hacer cosas como lo que pediste.

Este concepto se usa en aplicaciones web MVC .

.NET Framework 4.x proporciona varios atributos que desencadenan acciones, por ejemplo: ExceptionFilterAttribute (manejo de excepciones), AuthorizeAttribute (manejo de autorizaciones). Ambos se definen en System.Web.Http.Filters .

Por ejemplo, podría definir su propio atributo de autorización de la siguiente manera:

 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 } } 

Luego, en su clase de controlador , decorará los métodos que se supone que usan su autorización de la siguiente manera:

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

Siempre que se invoque el método Post , se llamará al método IsAuthorized dentro de myAuthorization Attribute antes de que se myAuthorization el código dentro del método Post .

Si devuelve false en el método IsAuthorized , IsAuthorized que no se concede la autorización y se cancela la ejecución del método Post .


Para comprender cómo funciona esto, veamos un ejemplo diferente: ExceptionFilter , que permite filtrar excepciones mediante el uso de atributos, el uso es similar al mostrado anteriormente para AuthorizeAttribute (aquí puede encontrar una descripción más detallada sobre su uso).

Para usarlo, DivideByZeroExceptionFilter clase DivideByZeroExceptionFilter de ExceptionFilterAttribute como se muestra aquí , y anule el método 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 }; } } } 

A continuación, use el siguiente código de demostración para activarlo:

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

Ahora que sabemos cómo se usa, estamos interesados ​​principalmente en la implementación. El siguiente código es de .NET Framework. Utiliza la interfaz IExceptionFilter internamente como un contrato:

 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); } } 

El ExceptionFilterAttribute sí se define de la siguiente manera:

 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. 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(); } } } 

Dentro de ExecuteExceptionFilterAsync , se llama al método OnException . Debido a que lo ha anulado como se muestra anteriormente, ahora su código puede manejar el error.


También hay un producto comercial disponible como se menciona en la respuesta de OwenP , PostSharp , que le permite hacerlo fácilmente. Aquí hay un ejemplo de cómo puedes hacer eso con PostSharp. Tenga en cuenta que hay una edición Express disponible que puede usar gratis incluso para proyectos comerciales.

Necesitas algún tipo de marco orientado al aspecto. PostSharp lo hará, al igual que Windsor .

Básicamente, subclasifican su objeto y anulan este método …

entonces se convierte en:

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

por supuesto, todo esto está oculto para ti. Todo lo que tiene que hacer es preguntarle a Windsor por el tipo, y lo hará por usted. El atributo se convierte en una instalación (personalizada), creo que en Windsor.

Puede usar ContextBoundObject y IMessageSink. Ver http://msdn.microsoft.com/nb-no/magazine/cc301356(en-us).aspx

Tenga en cuenta que este enfoque tiene un impacto severo en el rendimiento en comparación con una llamada directa al método.

No creo que haya una manera de hacerlo con solo un atributo, pero al usar clases de proxy y reflexión podría tener una clase que sepa interceptar instancias de las clases en las que tiene métodos atribuidos.

Luego, la clase proxy puede activar un evento siempre que se invoquen los métodos atribuidos.

Un atributo proporciona información, son metadatos. No sé de una manera de hacerlo de forma espontánea, alguien podría.

Puede ver los métodos parciales en .NET que le permiten hacer un manejo ligero de eventos. Proporciona los ganchos y deja que otra persona maneje la implementación. Si el método no está implementado, el comstackdor simplemente lo ignora.

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

Puede echar un vistazo a la solución del pobre: ​​ver el patrón decorador.