C #: Comment créer un attribut sur une méthode déclenchant un événement quand il est appelé?

StackOverflow https://stackoverflow.com/questions/226420

Question

Existe-t-il un moyen en C # ou .NET en général de créer un attribut sur une méthode qui déclenche un événement lorsque la méthode est appelée? Idéalement, je pourrais exécuter des actions personnalisées avant et après l’appel de la méthode.

Je veux dire quelque chose comme ça:

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

Je ne sais absolument pas comment faire ou si c'est possible, mais System.Diagnostic.ConditionalAttribute peut faire la même chose en arrière-plan. Je ne suis pas sûr cependant.

MODIFIER : j'ai oublié de mentionner qu'en raison des circonstances propres à mon cas, les performances ne sont pas vraiment un problème.

Était-ce utile?

La solution

La seule façon pour moi de procéder consiste à utiliser PostSharp . Il post-traite votre IL et peut faire des choses comme ce que vous aviez demandé.

Autres conseils

Ce concept est utilisé dans les applications MVC .

.NET Framework 4.x fournit plusieurs attributs qui déclenchent des actions, par exemple: ExceptionFilterAttribute (gestion des exceptions), AuthorizeAttribute (autorisation de gestion ). Les deux sont définis dans System.Web.Http.Filters .

Vous pouvez par exemple définir votre propre attribut d'autorisation comme suit:

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
    }

}

Ensuite, dans votre classe contrôleur , vous décorez les méthodes censées utiliser votre autorisation de la manière suivante:

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

Chaque fois que la méthode Post est appelée, elle appelle la méthode IsAuthorized dans l'attribut myAuthorization avant . code dans la méthode Post est exécuté.

Si vous renvoyez false dans la méthode IsAuthorized , vous signalez que l'autorisation n'est pas accordée et que l'exécution de la méthode Post est annulée.

Pour comprendre comment cela fonctionne, examinons un exemple différent: ExceptionFilter , qui permet de filtrer les exceptions à l'aide d'attributs. Son utilisation est similaire à celle présentée ci-dessus pour AuthorizeAttribute (vous pouvez trouver une description plus détaillée de son utilisation ici ).

Pour l'utiliser, dérivez la classe DivideByZeroExceptionFilter de ExceptionFilterAttribute comme indiqué ici , et substitue la méthode 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
                };
        }
    }
}

Utilisez ensuite le code de démonstration suivant pour le déclencher:

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

Maintenant que nous savons comment l'utiliser, nous nous intéressons principalement à la mise en œuvre. Le code suivant provient du .NET Framework. Il utilise l'interface IExceptionFilter en interne en tant que contrat:

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

Le ExceptionFilterAttribute lui-même est défini comme suit:

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

Dans ExecuteExceptionFilterAsync , la méthode OnException est appelée. Comme vous l'avez remplacé comme indiqué précédemment, l'erreur peut désormais être traitée avec votre propre code.

Un produit commercial est également disponible, comme indiqué dans la réponse de OwenP, PostSharp , ce qui vous permet de le faire. aussi facilement. Voici un exemple d'utilisation de PostSharp. Notez qu’il existe une édition Express disponible que vous pouvez utiliser gratuitement, même pour des projets commerciaux.

Exemple PostSharp (voir le lien ci-dessus pour une description complète):

public class CustomerService
{
    [RetryOnException(MaxRetries = 5)]
    public void Save(Customer customer)
    {
        // Database or web-service call.
    }
}

Ici, l'attribut indique que la méthode Save est appelée jusqu'à 5 fois si une exception se produit. Le code suivant définit cet attribut personnalisé:

[PSerializable]
public class RetryOnExceptionAttribute : MethodInterceptionAspect
{
    public RetryOnExceptionAttribute()
    {
        this.MaxRetries = 3;
    }

    public int MaxRetries { get; set; }

    public override void OnInvoke(MethodInterceptionArgs args)
    {
        int retriesCounter = 0;

        while (true)
        {
            try
            {
                args.Proceed();
                return;
            }
            catch (Exception e)
            {
                retriesCounter++;
                if (retriesCounter > this.MaxRetries) throw;

                Console.WriteLine(
                    "Exception during attempt {0} of calling method {1}.{2}: {3}",
                    retriesCounter, args.Method.DeclaringType, args.Method.Name, e.Message);
            }
        }
    }
}

Vous avez besoin d’une sorte de cadre orienté Aspect. PostSharp le fera, de même que Windsor .

Fondamentalement, ils sous-classent votre objet et redéfinissent cette méthode ...

alors il devient:

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

     _innerClass.DoSomeStuff();
}

bien sûr, tout cela vous est caché. Tout ce que vous avez à faire est de demander le type à Windsor, qui le fera pour vous. L’attribut devient une installation (personnalisée) à Windsor, je pense.

Vous pouvez utiliser ContextBoundObject et IMessageSink. Voir http://msdn.microsoft.com/nb- no / magazine / cc301356 (fr-fr) .aspx

Soyez averti que cette approche a un impact important sur les performances par rapport à un appel direct à une méthode.

Je ne pense pas qu'il soit possible de le faire avec un simple attribut, mais en utilisant classes proxy et à la réflexion, vous pourriez avoir une classe qui sait intercepter les instanciations des classes dans lesquelles vous avez attribué des méthodes.

Ensuite, la classe proxy peut déclencher un événement chaque fois que les méthodes attribuées sont appelées.

Un attribut donne des informations, ce sont des métadonnées. Je ne connais pas de moyen de le faire sans attendre, quelqu'un pourrait le faire.

Vous pouvez examiner des méthodes partielles dans .NET qui vous permettent de gérer des événements légers. Vous fournissez les points d'ancrage et laissez quelqu'un d'autre gérer la mise en œuvre. Si la méthode n'est pas implémentée, le compilateur l'ignore.

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

Vous pouvez jeter un oeil à la solution du pauvre homme: voir le motif de décorateur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top