tags:

views:

231

answers:

6

I'm looking for a way to ensure that method 'A' calls method 'B'. So roughly, the deal is..

class one
{
    internal static void MethodA()
    {
        //Do Something here.  SHOULD be calling B.
    }

    void MethodB()
    {
         //MUST be called by MethodA.
    }
}

class two
{
    internal void MethodA()
    {
        //Test that MethodA calls MethodB
    }
}

I should point out that i'm stuck on .Net 2.0 for this one so ExpressionTrees are a no go. I'm not even sure they'd help but that was my initial thought.

EDIT: This is for an internal tool to visualize cyclomatic complexity of the source so I'm not concerned with breaking encapsulation here. Also,..it's likely this is going to have to be done just using System.Reflection.

+1  A: 

Maybe not exactly what you're looking for, but you could write unit tests using a mocking framework. That mocking framework (like Moq, for instance), can verify that a method has been called in the mock when exercising some code.

mgroves
How I'd like to use Moq. I use it personally but can't at work.This one's gotta be done using just the framework I'm afraid.
Stimul8d
+2  A: 

You could use Mono Cecil to reflect on the method body of MethodA and look for call/callvirt instructions which call MethodB.

Pent Ploompuu
Hmmm, I can't use Mono but you put me on to the MethodBody class. Never came across that before. Could be useful.
Stimul8d
If you can't use any external components then you could do some minimal parsing of the `MethodBody` to find the `call/callvirt` instructions and the metadata token for those instructions and then use `Module.ResolveMethod`.
Pent Ploompuu
btw you don't need to use Mono if you only want to use Mono Cecil, it works very well on .net 2.0 also.
Pent Ploompuu
+1  A: 

To be honest, I think you're being too nosy about implementation of class' internal details.

As for the answer, I provide a counterpoint, consider:

void MethodB() {
    var methodA = BuildDelegate("Method" + "A");
    methodA();
}
Pasi Savolainen
Agreed,...WAY to nosy but that's kinda the point of the app in this case,..this isn't production code or unit testing,..it's an internal tool.
Stimul8d
+1  A: 

can't you hand roll a mock object? It depends on if you can make MethodB visible and virtual.

class one
{
    internal static void MethodA()
    {
        //Do Something here.  SHOULD be calling B.
    }

    internal virtual void MethodB()
    {
         //MUST be called by MethodA.
    }
}

class three : one
{
    public bool wasCalled;
    override void MethodB()
    {
         wasCalled=true;
    }
}


class two
{
    internal void MethodA()
    {
        three.ClassA();
        if (three.wasCalled)
        {
        }
    }
} 
Sam Holder
I COULD do that. But that'd take some serious effort on my part in this case since the mocks would need to be dynamically created. I need to rework this question I think. I'm looking at visualizing cyclomatic complexity in this tool,..and I didn't get that across in the question.
Stimul8d
+2  A: 

There is a codeproject snippet for an IL reader, you might have to patch it for generics support though.

http://www.codeproject.com/KB/cs/sdilreader.aspx

Have fun!

Florian Doyon
I just came across that very same article after looking at the MethodBody class. Good call though. Have a big green tick.
Stimul8d
+1  A: 

This is doable in many cases, but is going to take some typing on your part. The first thing you want to do is use an ILReader class - there's an outline of one here (and the one that Florian Doyon posted). Then you want to wrap that in a class like this:

public class MethodCalls : IEnumerable<MethodInfo>
{
    MethodBase _method;

    public MethodCalls(MethodBase method)
    {
        _method = method;
    }

    public IEnumerator<MethodInfo> GetEnumerator()
    {
        // see here: http://blogs.msdn.com/haibo_luo/archive/2005/10/04/476242.aspx
        ILReader reader = new ILReader(_method);

        foreach (ILInstruction instruction in reader) {
            CallInstruction call = instruction as CallInstruction;
            CallVirtInstruction callvirt = instruction as CallVirstInstruction;
            if (call != null)
            {
                yield return ToMethodInfo(call);
            }
            else if (callvirt != null)
            {
                yield return ToMethodInfo(callvirt);
            }
        }
    }
}

MethodInfo ToMethodInfo(CallInstruction instr) { /* ... */ }

MethodInfo ToMethodInfo(CallVirtInstruction instr) { /* ... */ }

Neither of the two flavors of ToMethodInfo are defined because CallInstruction is not defined either. Nonetheless, this outline will hopefully turn your problem into:

public static bool Calls(MethodBase caller, MethodInfo callee)
{
    return new MethodCalls(caller).Contains(callee);
}
plinth
My my,..aren't you a clever badger! Really helpful, thanks.
Stimul8d