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
public class Expr { public static void Test<T>(Expression<Action<T>> action) { // don't care about parameters!!! } }
Inside Test
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
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.