Dealing with parameters in expressions and strongly-typed reflection

Something that always bothered me using Expression trees for strongly-typed reflection were the weirdness of doing reflection for methods that return parameters.  Expression trees and reflection go hand-in-hand when doing fluent interfaces/internal DSLs.  Suppose we want to do strongly-typed reflection over this class:

public class SomeClass
{
    public int SomeMethod(string one, string two)
    {
        return 5;
    }

    public void SomeOtherMethod(string one, string two)
    {
        
    }
}

THE MOST INTERESTING CLASS IN THE WORLD.  For reflection, we have a couple of choices, one where we just use Action<T> as the expression delegate type:

public class Expr
{
    public static void Test<T>(Expression<Action<T>> action)
    {
        // don't care about parameters!!!
    }
}

Inside Test<T>, we never execute the Action, but walk the expression tree to find what we’re interested in.  For this to work, we still have to call the method inside our lambda with some arbitrary parameters:

Expr.Test<SomeClass>(x => x.SomeMethod("one", "two"));

In many cases, I actually could care less about the values of the parameters, and I’ll often just pass in a bunch of nulls or default values to get things to work.  The issue comes in with methods with a ton of parameters, which can happen sometimes in things like MVC controller actions.  The other choice is to use something like this, where instead of an Action<T>, which lets me put anything under the sun in it, I specify I want a specific Func or Action, and create a bunch of overloads to handle different return values and parameter values:

public static void Method<TType, T0, T1>(Expression<Func<TType, T0, T1, object>> method)
{
}
public static void Method<TType, T0, T1>(Expression<Action<TType, T0, T1>> method)
{
}

Because there is no first-class Void type, I have to declare an Action and a Func.  The usage now seems much more verbose:

Expr.Method<SomeClass, string, string>((o, p1, p2) => o.SomeMethod(p1, p2));
Expr.Method<SomeClass, string, string>((o, p1, p2) => o.SomeOtherMethod(p1, p2));

While still refactoring-safe (as I don’t have the method names as strings anywhere), is this really an improvement?  Offhand, I don’t think so, it looks much more verbose just to get at that one method.  In the first example, the types of the parameters would work to choose the right method call, as you could create overloads with the same number of parameters, but different return types.  In the second example, refactoring still gets a little funky if we’re doing things like changing the signature.  Not to mention, the call is frickin’ ginormous.

Where I was going with this is my aversion to all of the string-happy ASP.NET MVC code around controller and action names.  Nothing raises a red flag more than a class or method name hard-coded as a string.  That kind of code is a time-bomb in the face of changing a controller or action name.  Anything that might create an aversion to changing names of classes or members is absolutely insidious and needs to be stamped out with a giant lambda boot.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in C#. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Andy Alm

    In order to not specify the parameters of the method, could you do something like this:

    Expr.Method>(o => o.SomeMethod);

    Given all the generic parameters you have to specify, that may not be any better. Just thought I would throw that out there. I did a little test and it seemed to work. Here is the code:

    public static class Expr
    {
    public static string Method (Expression> methodExpression)
    where TDelegate : ICloneable,ISerializable //you cannot limit a generic parameter to be a Delegate, but this gets us closer.
    {
    return GetMethodName((UnaryExpression)methodExpression.Body);
    }

    public static string Method(Expression> methodExpression)
    {
    return GetMethodName((UnaryExpression) methodExpression.Body);
    }

    private static string GetMethodName(UnaryExpression unaryExpression)
    {
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
    var constantExpression = (ConstantExpression)methodCallExpression.Arguments[2];
    var method = (MethodInfo)constantExpression.Value;

    return method.Name;
    }
    }

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Andy

    I like the right part, but I wish the compiler would just…figure out the left part. We’ve just ignored it for now, accepting that no matter what we do, it’ll be ugly. Just trying to find the least ugly way…