Заказ выполнения азартных сообщений

Хорошо, это может затянуться. Я пытаюсь сделать две вещи:

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

  • Я также хочу перехватить все вызовы методов и сделать что-то.

Выполнение обоих на своих работах отлично. Сочетание их, кажется, работает только в одном порядке исполнения, и, как это делает Мерфи, он неправильный (по крайней мере для меня).

Я хотел бы сначала добавить композицию, чтобы перехват всех вызовов также перехватил те, которые были предварительно введены.

namespace ConsoleApplication13
{
  using System;
  using System.Reflection;

  using PostSharp;
  using PostSharp.Aspects;
  using PostSharp.Aspects.Dependencies;
  using PostSharp.Extensibility;

  [Serializable]
  [ProvideAspectRole("COMPOSER")]
  public sealed class ComposeAspectAttribute : CompositionAspect
  {
    [NonSerialized]
    private readonly Type interfaceType;

    private readonly Type implementationType;

    public ComposeAspectAttribute(Type interfaceType, Type implementationType)
    {
      this.interfaceType = interfaceType;
      this.implementationType = implementationType;
    }

    // Invoked at build time. We return the interface we want to implement. 
    protected override Type[] GetPublicInterfaces(Type targetType)
    {
      return new[] { this.interfaceType };
    }

    // Invoked at run time. 
    public override object CreateImplementationObject(AdviceArgs args)
    {
      return Activator.CreateInstance(this.implementationType);
    }
  }

  [Serializable]
  [ProvideAspectRole("INTERCEPTOR")]
  [MulticastAttributeUsage(MulticastTargets.Method)]
  [AspectRoleDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, "COMPOSER")]
  public sealed class InterceptAspectAttribute : MethodInterceptionAspect
  {
    public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
    {
      base.CompileTimeInitialize(method, aspectInfo);

      // Warning in VS output
      Message.Write(method, SeverityType.Warning, "XXX", "Method: " + method.Name);
    }

    public override void OnInvoke(MethodInterceptionArgs args)
    {
      Console.WriteLine("Intercepted before");
      args.Proceed();
      Console.WriteLine("Intercepted after");
    }
  }

  interface ITest
  {
    void Call();
  }

  class TestImpl : ITest
  {
    public void Call()
    {
      Console.WriteLine("CALL remote implemented");
    }
  }

  [InterceptAspect(AspectPriority = 1)]
  [ComposeAspect(typeof(ITest), typeof(TestImpl), AspectPriority = 2)]
  class Test
  {
    // this should, after compilation, have all methods of ITest, implemented through an instance of TestImpl, which get intercepted before TestImpl is called

    public void CallLocalImplementedTest()
    {
      Console.WriteLine("CALL local implemented");
    }
  }


  class Program
  {
    static void Main()
    {
      var test = new Test();

      ITest t = Post.Cast<Test, ITest>(test);

      Console.WriteLine("TEST #1");
      t.Call();

      Console.WriteLine("TEST #2");
      test.CallLocalImplementedTest();

      Console.ReadLine();
    }
  }
}

Я попытался повлиять на порядок выполнения двух аспектов

  • AspectRoleDependency, заставляя перехватчик зависеть от того, чтобы композитор запускался первым

  • AspectPriority, также делая первый запуск композитора.

Поскольку тесты всегда дают

TEST #1
CALL remote implemented

TEST #2
Intercepted before
CALL local implemented
Intercepted after

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

Ответ 1

С текущими аспектами и текущей настройкой вы не сможете достичь желаемого результата.

Проблема заключается в том, как работает служба Postsharp: он выполняет размахивание IL за один шаг и применяет только InterceptAspect к методам, которые присутствуют в исходное время компиляции, поэтому он не видит новые реализации интерфейса добавлены с помощью ComposeAspect.

Таким образом, никакой порядок принятия или предоставления ролей, приоритетов или другой конфигурации не поможет.

Одним из способов решения проблемы было бы добавить InterceptAspect для введенного класса TestImpl:

[InterceptAspect]
class TestImpl : ITest
  {
    public void Call()
    {
      Console.WriteLine("CALL remote implemented");
    }
  }

В этом случае логика регистрации будет добавлена ​​напрямую TestImpl, поэтому этот метод будет содержать журнал, когда он будет скомпонован в ваш класс Test.

Или, если вы не отмечаете каждую реализацию, вы можете поместить свой аспект на сам интерфейс:

[InterceptAspect(AttributeInheritance = MulticastInheritance.Multicast)]
interface ITest
{
   void Call();
}