2

I'm testing the performance of some similar method calls that I'm wrapping in some timing and logging statements. I'm passing these methods in via an Action delegate parameter.

Is there any way to print details about the call?

For example:

var httpResult = TestService(() => serviceHttp.Search(criteria));
var tcpResult = TestService(() => serviceTcp.Search(criteria));
var localResult = TestService(() => servicelocal.Search(criteria));

...

private static double TestService(Action serviceOperation)
{
    const int iterations = 15;
    ...

    for (var i = 0; i < iterations; i++)
    {
        var watch = Stopwatch.StartNew();

        ...

        Console.WriteLine(string.Format("{0} ElapsedMilliseconds={1}", ????, watch.ElapsedMilliseconds));
         // Ideally this would print something like "serviceTcp.DoStuff(...) ElapsedMilliseconds=313"
    }

    ...
}
Brandon Linton
  • 4,373
  • 5
  • 42
  • 63
  • I know if you treat it as an Expression object, you can pull out some info. See: http://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression – Chris Sinclair May 08 '12 at 22:06
  • Hmm.. You will need to change the type of serviceOperation. It's not possible to convert an Action (delegate) into an Expression tree. – Strillo May 08 '12 at 22:23
  • Clearly you'll want to at least use an `Action` delegate here, now it is simple to have the caller provide context. – Hans Passant May 08 '12 at 22:44

2 Answers2

4

Change your testing method declaration to

private static double TestService(Expression<Action> expression)

Call Compile method of expression object to get a method for testing:

var serviceOperation = expression.Compile();

Expression object can provide a lot of information about method call, you can start with something like this:

private static string GetMethodCallDescription(Expression<Action> expression)
{
    var mce = (MethodCallExpression)expression.Body;
    var method = mce.Method;
    var sb = new StringBuilder();
    sb.Append(method.DeclaringType.Name);
    sb.Append(".");
    sb.Append(method.Name);
    sb.Append("(");
    bool firstarg = true;
    foreach(var arg in mce.Arguments)
    {
        if(!firstarg)
        {
            sb.Append(", ");
        }
        else
        {
            firstarg = false;
        }
        sb.Append(arg.ToString());
    }
    sb.Append(")");
    return sb.ToString();
}
max
  • 33,369
  • 7
  • 73
  • 84
2

You could do it without using expression trees; just change the signature of TestService to take the action and the parameter separately, and use the Delegate.Target and Delegate.Method properties to get the type and method:

var httpResult = TestService(serviceHttp.Search, criteria);
var tcpResult = TestService(serviceTcp.Search, criteria);
var localResult = TestService(servicelocal.Search, criteria);

...

private static double TestService<T>(Action<T> serviceOperation, T parameter)
{
    const int iterations = 15;
    ...

    for (var i = 0; i < iterations; i++)
    {
        var watch = Stopwatch.StartNew();

        ...

        string typeName = serviceOperation.Method.IsStatic
                          ? serviceOperation.Method.DeclaringType.Name
                          : serviceOperation.Target.GetType().Name;
        string methodName = serviceOperation.Method.Name;
        Console.WriteLine(string.Format("{0}.{1} ElapsedMilliseconds={2}", typeName, methodName, watch.ElapsedMilliseconds));
         // Ideally this would print something like "serviceTcp.DoStuff(...) ElapsedMilliseconds=313"
    }

    ...
}
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758