Ответ 1
Единственный способ, которым я знаю, как это сделать: PostSharp. Он пост-обрабатывает ваш IL и может делать то, что вы просили.
Есть ли способ в С# или .NET вообще создать атрибут метода, который запускает событие при вызове метода? В идеале я мог бы запускать пользовательские действия до и после вызова метода.
Я имею в виду что-то вроде этого:
[TriggersMyCustomAction()]
public void DoSomeStuff()
{
}
Я совершенно не знаю, как это сделать, или если это вообще возможно, но System.Diagnostic.ConditionalAttribute может сделать аналогичную вещь в задний план. Я не уверен, хотя.
EDIT. Я забыл упомянуть, что из-за обстоятельств моего конкретного случая производительность на самом деле не проблема.
Единственный способ, которым я знаю, как это сделать: 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
.
Он использует интерфейс 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, которую вы можете использовать бесплатно даже для коммерческих проектов.
Вам нужна какая-то структура, ориентированная на ASP. PostSharp сделает это, как Windsor.
В принципе, они подклассифицируют ваш объект и переопределяют этот метод...
тогда он становится:
//proxy
public override void DoSomeStuff()
{
if(MethodHasTriggerAttribute)
Trigger();
_innerClass.DoSomeStuff();
}
конечно, все это скрыто для вас. Все, что вам нужно сделать, это попросить Windsor для этого типа, и он сделает проксирование для вас. Атрибут становится (обычным) средством, которое я думаю в Виндзоре.
Вы можете использовать ContextBoundObject и IMessageSink. См. http://msdn.microsoft.com/nb-no/magazine/cc301356(en-us).aspx
Будьте предупреждены, что этот подход имеет сильное влияние на производительность по сравнению с прямым вызовом метода.
Я не думаю, что есть способ сделать это с помощью всего лишь атрибута, но используя прокси-классы и размышления, у вас может быть класс, который знает, чтобы перехватывать экземпляры классов, в которых вы приписывали методы.
Затем класс прокси-сервера может инициировать событие всякий раз, когда вызываемые методы вызываются.
Атрибут дает информацию, это метаданные. Я не знаю, как это сделать, кто-то может.
Вы можете посмотреть частичные методы в .NET, которые позволяют вам выполнять легкую обработку событий. Вы предоставляете крючки и позволяете кому-то другому справляться с реализацией. Если метод не реализован, компилятор просто игнорирует его.