C#:Как создать атрибут для метода, запускающего событие при его вызове?

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

Вопрос

Есть ли способ в C # или .NET в целом создать атрибут для метода, который запускает событие при вызове метода?В идеале я мог бы запускать пользовательские действия до и после вызова метода.

Я имею в виду что-то вроде этого:

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

Я совершенно не представляю, как это сделать и возможно ли это вообще, но Система.Диагностика.Условный атрибут мог бы проделать нечто подобное в фоновом режиме.Хотя я не уверен.

Редактировать:Я забыл упомянуть, что в силу обстоятельств моего конкретного случая производительность на самом деле не является проблемой.

Это было полезно?

Решение

Единственный способ, которым я знаю, как это сделать, - это PostSharp . Он обрабатывает ваш IL и может делать то, что вы просили.

Другие советы

Эта концепция используется в MVC веб-приложения.

Тот Самый .NET Framework 4.x предоставляет несколько атрибутов, которые запускают действия, например: ExceptionFilterAttribute (обработка исключений), AuthorizeAttribute (разрешение на обработку).Оба они определены в System.Web.Http.Filters.

Вы могли бы, например, определить свой собственный атрибут авторизации следующим образом:

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
    }

}

Затем, в вашем контроллер класс, в котором вы описываете методы, которые должны использовать вашу авторизацию, следующим образом:

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

Всякий раз, когда Post будет вызван метод, он вызовет IsAuthorized метод внутри myAuthorization Атрибут до того, как код внутри Post метод выполнен.

Если ты вернешься false в IsAuthorized метода, вы сигнализируете о том, что авторизация не предоставлена, и выполнение метода прекращается. Post прерывается.


Чтобы понять, как это работает, давайте рассмотрим другой пример:Тот Самый ExceptionFilter, который позволяет фильтровать исключения с помощью атрибутов, использование аналогично показанному выше для AuthorizeAttribute (вы можете найти более подробное описание его использования здесь).

Чтобы использовать его, выведите DivideByZeroExceptionFilter класс из ExceptionFilterAttribute как показано на рисунке здесь, и переопределите метод 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
                };
        }
    }
}

Затем используйте следующий демонстрационный код, чтобы запустить его:

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

Теперь, когда мы знаем, как это используется, нас в основном интересует реализация.Следующий код взят из .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);
    }
}

Тот Самый 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();
        }
    }
}

Внутри ExecuteExceptionFilterAsync, способ OnException называется.Поскольку вы переопределили его, как показано ранее, ошибка теперь может быть обработана вашим собственным кодом.


Существует также доступный коммерческий продукт, как упомянуто в ответе OwenP, Постшарповый, что позволяет вам сделать это легко. Здесь вот пример того, как вы можете сделать это с помощью PostSharp.Обратите внимание, что доступна экспресс-версия, которую вы можете использовать бесплатно даже для коммерческих проектов.

Пример PostSharp (смотрите ссылку выше для получения полного описания):

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

Здесь атрибут указывает, что Save метод вызывается до 5 раз, если возникает исключение.Следующий код определяет этот пользовательский атрибут:

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

Вам нужен какой-то аспектно-ориентированный фреймворк. PostSharp сделает это, как и Windsor .

По сути, они наследуют ваш объект и переопределяют этот метод ...

тогда это становится:

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

     _innerClass.DoSomeStuff();
}

Конечно, все это скрыто от вас. Все, что вам нужно сделать, это спросить Виндзор о типе, и он сделает прокси для вас. Атрибут становится (пользовательским) средством, которое я думаю в Виндзоре.

Вы можете использовать ContextBoundObject и IMessageSink. См. http://msdn.microsoft.com/nb- нет / журнал / cc301356 (EN-US) .aspx

Имейте в виду, что этот подход сильно влияет на производительность по сравнению с прямым вызовом метода.

Я не думаю, что есть способ сделать это только с помощью атрибута, но используя прокси-классы и отражение у вас может быть класс, который знает, что он перехватывает экземпляры классов, которым вы приписали методы.

Затем прокси-класс может инициировать событие при каждом вызове атрибутивных методов.

Атрибут дает информацию, это метаданные. Я не знаю, как это сделать, кто-то может.

Вы можете посмотреть на частичные методы в .NET, которые позволяют вам выполнять некоторую облегченную обработку событий. Вы предоставляете хуки и позволяете кому-то другому заниматься реализацией. Если метод не реализован, компилятор просто игнорирует его.

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

Вы могли бы взглянуть на решение бедняка: см. образец декоратора.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top