Linq beyond queries: strong-typed reflection!

Note: this entry has moved.

Surely you know Linq brings the standard query operators to make queries a first class concept in the C# language. What you may or may not have realized, is that Linq can be used for way more than just querying. Here goes my first exploration outside the query bounds with Linq.

The problem with reflection: bye-bye type safety

There's (at least for me) a sense of comfort and reassurance in using a type-safe language like C# every time I hit Ctrl + Shift + B (or Shift + F6) - that is, when I compile ;). I know that weird and hard to debug runtime errors because of wrong type usage will almost never happen, as well as "missing method" or something like that. That's until I use reflection and get the infamous TargetInvocationException or some NullReferenceException if I'm not careful. Here's what I mean:

MethodInfo method = typeof(Mock).GetMethod("PublicMethodParameters",
                        new Type[] { typeof(string), typeof(int) }));


What happens if a refactoring is applied to the Mock class to rename the method? What happens if the type of a parameter changes? Yeeessss, runtime exceptions! This is not a trivial problem if you have a flexible and pluggable framework that uses lots of reflection. Not being able to apply refactorings (or making it too expensive) because of the risk of breaking things, essencially limits your ability to improve the design and evolve your code. So, how about this to replace the magic strings and loosely typed Type arrays?:

MethodInfo info = Reflector.Method<Mock, string, int>(

     (x, y, z) => x.PublicMethodParameters(y, z));

Strong-typed reflection via Linq

The working principle is that lamda expressions passed as arguments (just as delegates in previous versions of .NET) are not necessarily executed.

The code above basically constructs a lambda expression with a calls to a given method on a type. The type of the target type that declares the method is the first type parameter to the Method<> static generic method. The optional type parameters that you can specify will be the types of the arguments (if any) of the method you will call. If I were to retrieve the MethodInfo for a parameterless method, the expression would be:

MethodInfo info = Reflector.Method<Mock>(x => x.PublicMethodNoParameters());

This is the more typical lambda that you may be used to seeing. In a lambda expression, if you need to pass additional arguments, you must enclose all of them in parethesis (x, y, z in the example above). I got the same typed reflection feature working for properties and fields:

PropertyInfo property = Reflector.Property<Mock>(x => x.PublicProperty);

FieldInfo field = Reflector.Field<Mock>(x => x.PublicField);

Leveraging expression trees

Now to the interesting part. How is it implemented?

In Linq, any method receiving a lambda expression (a delegate type) can be converted into a method receiving an Expression<T> for the same delegate type, without changes to client code. For example:

private static void DoSomething(Predicate<Mock> predicate)

can be replaced with:

private static void DoSomething(Expression<Predicate<Mock>> predicate)

In both cases, calling code can be the same lambda expression:

DoSomething(x => x.Value > 25);

What happens is that the compiler, instead of passing in the pointer to the anonymous delegate in the second method signature, generates IL code that builds an AST (abstract syntax tree) of the code, in the form of an expression tree. You can see this if you open Reflector (I named my typed reflection class after it as a tribute to the greatest tool any advanced developer should use regularly) and dissasemble the method invocation to DoSomething:

ParameterExpression expression1 = Expression.Parameter(typeof(Mock), "x");

Program.DoSomething(Expression.Lambda<Predicate<Mock>>(Expression.GT(Expression.Field(expression1, fieldof(Mock.Value)), Expression.Constant(0x19, typeof(int))), new ParameterExpression[] { expression1 }));

There you can see how the compiler built the entire expression using static methods on the Expression class (more on how I think the API could be improved in a separate entry). And of course, in your method implementation, you can inspect that same tree and do whatever you want with it. The latest Linq CTP includes a very cool visualizer to see what's in the expression tree once it reaches your method body at runtime:

Expression tree visualizer in action

It should be pretty obvious by now how I'm implementing the strong typed reflection: I'm receiving an expression tree, and searching it for the method call node (or member access, for properties and fields). Here's the implementation of the Method<> method:

    public static MethodInfo Method<TDeclaringType>(Expression<Operation> method)

    {

        return GetMethodInfo(method);

    }

 

    private static MethodInfo GetMethodInfo(Expression method)

    {

        LambdaExpression lambda = method as LambdaExpression;

        if (lambda == null) throw new ArgumentNullException("method");

 

        MethodCallExpression methodExpr = null;

 

        // Our Operation<T> returns an object, so first statement can be either

        // a cast (if method does not return an object) or the direct method call.

        if (lambda.Body.NodeType == ExpressionType.Cast)

        {

            // The cast is an unary expression, where the operand is the

            // actual method call expression.

            methodExpr = ((UnaryExpression)lambda.Body).Operand as MethodCallExpression;

        }

        else if (lambda.Body.NodeType == ExpressionType.MethodCall ||

            lambda.Body.NodeType == ExpressionType.MethodCallVirtual)

        {

            methodExpr = lambda.Body as MethodCallExpression;

        }

 

        if (methodExpr == null) throw new ArgumentException("method");

 

        return methodExpr.Method;

    }

The Operation delegate type is one that I created. I couldn't use the Linq Func<T> (and T, Arg0..) because they return a boolean. I needed something more flexible, basically something returning an object, and delegate "overloads" receiving a number of fixed argument types (same as Func<T>). So I got:

    public delegate object Operation();

    public delegate object Operation<T>(T declaringType);

    public delegate object Operation<T, A0>(T declaringType, A0 arg0);

    ...

Note that the user of our API never knows about these delegate types, just as the user of the query operators never knows about Func<T>. I expect these delegates to dissapear in the future, in favor of something more elegant (maybe public delegate object Operation <params T>  ;)). Also, notice how I'm adding the new argument type parameters *after* the T, which is the usual convention for overloading, and which is contrary to what Linq does in their Func<T>s.

Properties and fields are very similar to the example above, and not too interesting. The Method overloads, however, are the really nice part:

    public static MethodInfo Method<TDeclaringType>(Expression<Operation<TDeclaringType>> method)

    {

        return GetMethodInfo(method);

    }

    public static MethodInfo Method<TDeclaringType, A0>(Expression<Operation<TDeclaringType, A0>> method)

    {

        return GetMethodInfo(method);

    }

    public static MethodInfo Method<TDeclaringType, A0, A1>(Expression<Operation<TDeclaringType, A0, A1>> method)

    {

        return GetMethodInfo(method);

    }
    ...

Ok, what's the big deal, right? Well, by being able to specify the types of the arguments and then declaring them in the lambda, you basically get strong typing and compile-type safety back even if you are using reflection in the end :). Back to the initial example:

MethodInfo info = Reflector.Method<Mock, string, int>(

    (x, y, z) => x.PublicMethodParameters(y, z));

See how the y and z parameters are strong typed and are used to resolve at compile time which is the method "invoked" in the lambda expression. When the IDE is fully updated to work with C# 3.0, a rename refactor of the given method would yield the expected result: it will be renamed in all places like this where you are using it even for reflection! And if you change an argument type, the code will simply fail to compile!

Freakin' amazing :D

Get the prototype and play at will.

4 Comments

  • If your application framework relies on using reflection for extending it, then that application's architecture isn't properly designed.

    Interfaces and abstract classes should always be used in those cases.

    IMO reflection is abused way too often to provide a quick and dirty way to call into applications with poor planning.

  • Sure it can be abused. By try to come up with a dependency injection framework, AOP, a web service framework, etc. without reflection ;-). Or adding dynamic language features to a platform, for that matter.

  • Marc, the problem with your approach is that it's not compile-time safe, as you're still using a string for the method name. The compiler will not verify the method signature at all.

  • Correct (part of the way) in that the specific method is called out by name and thus it's signature can't be checked until runtime. That is it's strength, you can call it against any instance of a class (or derived class), for any method and it'll work. The Initialize method will throw an exception if the LCG cannot be built based on the supplied method name. You should arguably be doing this ONCE at "setup time", whatever that means for the application... e.g. for a business rule engine, you can do all the hookup at the time you read the rules definition and then know that the delegate is safe to call everywhere.

    Be sure not to miss the fact that all the call-sites ARE type checked if using the strong-type version of everything. It will simply not let you call a method with the wrong arguments for the delegate signature. The signature of the method that you specify in the delegate IS checked at compile time.

Comments have been disabled for this content.