Возможно, вы читали этот пост, который я написал некоторое время назад о чистом коде и использовании перехватчиков для выполнения некоторого аспектно-ориентированного программирования (АОП). Сегодня я хотел показать вам, как сделать то же самое с другим подходом, который называется техникой плетения IL. Это означает, что вы собираетесь использовать какой-нибудь инструмент для прикрепления кода сквозной резки к сборке при ее создании. таким образом ваш код будет добавлен в нужные вам места внутри вашей функции, пока вы строите свой проект, и ваш базовый код останется чистым.

В мире .Net есть ряд инструментов для IL Weaving, таких как PostSharp, Loom.net и Wicca, но теперь я собираюсь использовать PostSharp, чтобы показать вам, как это работает и чего с его помощью можно достичь.

IL Weaving имеет 2 основных этапа.

Сначала мы собираемся скомпилировать наше приложение в файл dll или exe, а затем запустить exe или dll через процессор AOP, и процессор определит, где эти аспекты должны быть добавлены в ваш код. Затем он генерирует новую кодовую базу на уровне IL и вставляет коды туда, где вы их настроили.

На выходе обработчика AOP Post снова будет dll или exe с тем же именем, которое вы ему передали. Разница в том, что весь код, связанный с вашими аспектами, основан на исходном коде, который вы передали.

Для этого нужно сделать два основных шага:

Шаг 1. Создайте класс, содержащий код нашего аспекта. Этот класс будет унаследован от базового класса, предоставляемого используемым нами фреймворком АОП (в моем случае это PostSharp).

Шаг 2. Присоедините этот класс аспекта к исходному коду.

Например, предположим, что у нас есть это простое консольное приложение, у которого есть основная функция, которая выглядит следующим образом:

namespace PostSharpSample 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
       try 
       { 
          var obj = new MyClass(); 
          obj.DoSomething("test AOP"); 
       } 
       catch (Exception) { } 
       Console.ReadKey(); 
    } 
} 
}

Теперь мы можем создать наш класс Aspect, и я назову его LoggingAspect.cs. Для этого сначала я добавлю в свой проект ссылку на dll PostSharp. (вы можете скачать PostSharp здесь или получить из пакетов nuget)

Этот класс будет унаследован от «OnMethodBoundaryAspect», который выглядит как шаблон декоратора. Этот класс находится в пространстве имен PostSharp.Aspects.

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

Это точки ловушки, которые вы можете использовать (PostSharp v 4.0.41)

public virtual void OnEntry(MethodExecutionArgs args);

Метод выполняется перед телом методов, к которым применяется этот аспект.

public virtual void OnException(MethodExecutionArgs args);

Метод выполняется после тела методов, к которым применяется этот аспект, в случае, если метод привел к исключению.

public virtual void OnExit(MethodExecutionArgs args);

Метод выполняется после тела методов, к которым применяется этот аспект.

public virtual void OnResume(MethodExecutionArgs args);

Метод, выполняемый, когда конечный автомат возобновляет выполнение после оператора yield return или await.

public virtual void OnSuccess(MethodExecutionArgs args);

Метод выполняется после тела методов, к которым применяется этот аспект, но только тогда, когда метод успешно возвращается (т. Е. Когда из метода не вылетает никакое исключение).

public virtual void OnYield(MethodExecutionArgs args);

Метод, выполняемый, когда конечный автомат выдает результат в результате оператора yield return или await.

Во всех этих методах параметр args предоставит вам информацию о методе, который вызывается из вашей базы кода, или об исключении и многие другие сведения, которые могут вам понадобиться внутри вашего класса аспекта.

Вот как наш класс Aspect будет выглядеть в первом фрагменте, переопределив 4 метода, упомянутых выше:

namespace PostSharpSample { 
[Serializable] [MulticastAttributeUsage(MulticastTargets.Method, TargetExternalMemberAttributes = MulticastAttributes.Instance)] public class LoggingAspect : OnMethodBoundaryAspect 
{ 
    public override void OnEntry(MethodExecutionArgs args) 
    { 
        Console.WriteLine("On Entry"); 
    } 
    public override void OnExit(MethodExecutionArgs args) 
    { 
        Console.WriteLine("On Exit"); 
    } 
    public override void OnException(MethodExecutionArgs args) 
    { 
        Console.WriteLine("On Exception"); 
    } 
    public override void OnSuccess(MethodExecutionArgs args) 
    { 
        Console.WriteLine("On Success"); 
    } 
} 
}

Обратите внимание, что для того, чтобы это работало, класс аспекта должен быть сериализуемым, поэтому мы добавляем атрибут Serializable к классу Aspect, как вы можете видеть в приведенном выше коде.

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

Прежде чем вы это сделаете, если вы запустите приложение, ничего не произойдет, и вы увидите следующее:

Поскольку мы используем IL Weaving, будет сложно сказать, прикреплен ли наш код аспекта к нашему базовому коду или нет. для этого мы могли бы использовать декомпиляторы, такие как ILSpy, и проверить там наш ассемблерный код.

Класс OnMethodBoundaryAspect наследуется от класса Attribute, а поскольку мы унаследовали от класса OnMethodBoundaryAspect, наш класс LoggingAspect уже является атрибутом.

Таким образом, прикрепить наш класс аспекта к нашему функциональному коду так же просто, как добавить атрибут [LoggingAspect] к любому классу, который мы хотим в нашем проекте.

namespace PostSharpSample { 
public class MyClass 
{ 
   [LoggingAspect] 
    public void DoSomething(string param1) 
    { 
       Console.WriteLine(param1); 
    } 
} 
}

Теперь, если вы скомпилируете свое приложение, вы увидите это как результат:

Если вы проверите выходную сборку с помощью ILSpy, вы увидите, что ваш код аспекта внедряется в те места, которые вы подключили в классе аспекта:

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

Если вы добавите этот атрибут на уровне класса, он будет прикреплен ко всем членам, включая функции, свойства и конструкторы, что может быть не тем, что вам нужно, поэтому вы можете явно определить, к каким типам членов вы хотите, чтобы он был прикреплен с другим атрибутом. называется «MultiCastAttributeUsage». скажем, если вы хотите, чтобы он был прикреплен только к методам, ваше оформление класса аспекта будет выглядеть следующим образом:

[Serializable] 
[MulticastAttributeUsage(MulticastTargets.Method, TargetExternalMemberAttributes = MulticastAttributes.Instance)] public class LoggingAspect : OnMethodBoundaryAspect 
{

Теперь в классе Assepmblyinfo.cs мы можем добавить эту строку, чтобы указать типы, к которым мы хотим присоединить этот класс аспекта, например:

[assembly: LoggingAspect(AttributeTargetTypes = "PostSharpSample.*")]

Это присоединит наш класс аспекта ко всем классам в пространстве имен PostSharpSample.

Вы можете найти этот образец проекта на Github здесь:

Https://github.com/BitsCorner/AOP-PostSharp

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