Search code examples
c#aoppostsharp

detect when "control flow" exits class


Assume I have a code:

class Module1 {
    public static void Main(string[] args) {
        Module1.level1();
    }
    public static void level1() {
        Module1.level2();
    }
    public static void level2() {
        Module2.level1();
    }
}

[DetectWhenFlowExitsClass]   // <-- note aspect
class Module2 {
    public static void level1() {
        Module2.level2();
    }
    public static void level2() {
        Module2.level3();
    }
    public static void level3() {
        throw new SystemException("oops");
    }
}

After calling Main() I get a stacktrace:

Unhandled Exception: System.SystemException: oops
   at Test.Module2.level3()
   at Test.Module2.level2()
   at Test.Module2.level1()
   at Test.Module1.level2()
   at Test.Module1.level1()
   at Test.Module1.Main(String[] args)

Question
How to write aspect which detects moment when "control flow" exits code of class Module2?
That is, when Test.Module2.level1() finishes its work [here, due to exception].

Exist any shortcuts for this in PostSharp?


Solution

  • The most basic way would be to use OnMethodBoundaryAspect, which allows you to handle the method entry and method exit advices. You will need to count number of method of each particular class on the stack and when this count goes from 1 to 0, the control is leaving methods of the aspected class.

    Here is the sample aspect code:

    [Serializable]
    public class DetectWhenFlowExitsClass : OnMethodBoundaryAspect
    {
        [ThreadStatic] private static Dictionary<Type, int> stackCounters;
    
        private Type declaringType;
    
        public override bool CompileTimeValidate(MethodBase method)
        {
            declaringType = method.DeclaringType;
            return true;
        }
    
        private void EnsureStackCounters()
        {
            if (stackCounters == null)
                stackCounters = new Dictionary<Type, int>();
        }
    
        public override void OnEntry(MethodExecutionArgs args)
        {
            EnsureStackCounters();
    
            int counter;
    
            stackCounters.TryGetValue(declaringType, out counter);
            stackCounters[declaringType] = ++counter;
        }
    
        public override void OnExit(MethodExecutionArgs args)
        {
            EnsureStackCounters();
    
            int counter;
    
            stackCounters.TryGetValue(declaringType, out counter);
            stackCounters[declaringType] = --counter;
    
            if (counter == 0)
                Console.WriteLine("Control leaving class {0}", declaringType.Name);
        }
    }
    

    You will probably need to tinker with this aspect implementation a bit, but it works in basic situations.