PostSharp: Как предотвратить вызов двойного аспекта при вызове базового метода? - PullRequest
1 голос
/ 17 мая 2019

Например:

class BaseClass : IDisposable {
   [LogMethod]
   public BaseClass() {
   }

   [LogMethod] // This is OnMethodBoundaryAspect with MulticastInheritance.Strict
   public virtual void Dispose() { // overridden method
   }
}

class SubClass : BaseClass {
    public SubClass() : base() { // this doesn't lead to double LogMethod invoking
    }

    public override void Dispose() { // overriding method
      base.Func(); // this leads to double LogMethod invoking
    }
}

Проблема в том, что base.Func() приводит к двойному вызову аспекта.Можно ли это предотвратить?

Ответы [ 2 ]

1 голос
/ 17 мая 2019

Это невозможно без изменения кода или внесения изменений в сам аспект.

Причина в том, что PostSharp преобразует код метода.Он добавляет вызовы аспекта в тело метода BaseClass.Dispose и в тело метода SubClass.Dispose.Поскольку BaseClass.Dispose вызывает SubClass.Dispose, аспект будет выполняться для обоих.

Чтобы отключить аспект, когда BaseClass.Dispose вызывается из SubClass.Dispose, необходимо передать информацию о вызывающем абоненте в базуметод.Без изменения кода (который нарушил бы цель аспекта), это возможно, изменив аспект для использования AsyncLocal или [ThreadStatic] для передачи состояния между экземплярами аспекта.

0 голосов
/ 21 мая 2019

Это, наверное, лучший способ. Я просто игнорирую вход в переопределенные методы.

    void IInstanceScopedAspect.RuntimeInitializeInstance() {
        IsEnabled = !IsOverridden( method, instance );
    }

    private static bool IsOverridden(MethodBase method, object instance) {
        if (method is MethodInfo method_) return instance.GetType().HasOverridingMethod( method_ );
        return false;
    }


    public static bool HasOverridingMethod(this Type type, MethodInfo baseMethod) {
        return type.GetOverridingMethod( baseMethod ) != null;
    }
    public static MethodInfo GetOverridingMethod(this Type type, MethodInfo baseMethod) {
        var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod;
        return type.GetMethods( flags ).FirstOrDefault( i => baseMethod.IsBaseMethodOf( i ) );
    }
    private static bool IsBaseMethodOf(this MethodInfo baseMethod, MethodInfo method) {
        return baseMethod.DeclaringType != method.DeclaringType && baseMethod == method.GetBaseDefinition();
    }
...