Аспектно-ориентированное программирование на C# с использованием PostSharp
Что такое АОП?
В любых крупных проектах появляются задачи, которые затрагивают множество различных классов. Например логирование, кеширование, профилирование и т.д. Чаще всего это приводит вот к такому коду:
AddToLog("Начало записи в файл.");
// Здесь какие-либо действия.
AddToLog("Конец записи в файл.");
Некрасиво, неудобно, легко забыть.
Парадигма аспектно-ориентированного программирования (АОП) предлагает выносить такую функциональность в отдельные классы (логично, не правда ли?) и подключать их к коду.
Для C# существует множество библиотек для АОП и они предлагают разные способы подключения дополнительной функциональности: Unity — конфиги, Spring.Net — создание прокси классов, PostSharp — атрибуты.
Подробнее про АОП можно почитать на википедии.
PostSharp
Сайт библиотеки
Библиотека платная, но есть бесплатная версия с урезанным функционалом. Ее нам будет вполне достаточно.
Скачиваем, подключаем к проекту. Приступаем к написанию кода.
Логирование
Для примера рассмотрим логирование запуска и завершения методов.
Создаем класс LoggingAttribute, наследуем его от OnMethodBoundaryAspect, для этого нужно будет подключить PostSharp.Aspects.
Переопределяем методы OnEntry, OnExit, OnException. В них будет обработка начала выполнения метода, завершения и генерация исключения в методе.
Получается вот такой класс:
using System;
using PostSharp.Aspects;
namespace AOPTestProject
{
[Serializable]
class LoggingAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Console.WriteLine("Method {0} started.", args.Method.Name);
}
public override void OnExit(MethodExecutionArgs args)
{
Console.WriteLine("Method {0} executed.", args.Method.Name);
}
public override void OnException(MethodExecutionArgs args)
{
Console.WriteLine("Method {0} raised {1}: {2}", args.Method.Name,
args.Exception.GetType().Name, args.Exception.Message);
}
}
}
Использование атрибута
Указываем атрибут у метода, который нас интересует.
[Logging]
private static float DivideInts(int x, int y)
{
return x / y;
}
static void Main(string[] args)
{
try
{
var result = DivideInts(6, 2);
var result1 = DivideInts(6, 0);
}
catch
{
}
Console.ReadLine();
}
Другие способы подключения атрибута
Понятно, что подключать атрибут к каждому методу неудобно. Вместо этого можно подключить его к классу — тогда он будет работать для всех методов класса.
А можно и подключить его прямо к сборке, например в файле AssemblyInfo.cs:
[assembly: AOPTestProject.Logging]
Теперь обработка включена для всех методов в сборке. Для нашего проекта результат будет выглядеть так:

Видно, что добавился вызов метода Main.

