С# Атрибут для запуска события при вызове метода

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

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

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

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

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

Ответ 1

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

Ответ 2

Эта концепция используется в 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.

Он использует интерфейс 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. Вы можете использовать его аналогично, как показано ранее для AuthorizeAttribute: Создайте свой собственный класс, наследуя от ExceptionFilterAttribute, и переопределите метод OnException.


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

Ответ 3

Вам нужна какая-то структура, ориентированная на ASP. PostSharp сделает это, как Windsor.

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

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

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

     _innerClass.DoSomeStuff();
}

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

Ответ 4

Вы можете использовать ContextBoundObject и IMessageSink. См. http://msdn.microsoft.com/nb-no/magazine/cc301356(en-us).aspx

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

Ответ 5

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

Затем класс прокси-сервера может инициировать событие всякий раз, когда вызываемые методы вызываются.

Ответ 6

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

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

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