LINQ beyond queries: Strong-typed refection

Surely you know LINQ brings the standard query operators to make queries a first class concept in the C# programming 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", newType[] { 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? Yes, 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 essentially 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 lambda expressions passed as arguments (just as delegates were passed in previous versions of .NET) are not necessarily executed.

The code above basically constructs a lambda expression with calls to a given method on a type. The type of 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 than 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:

 privatestaticvoid DoSomething(Predicate<Mock> predicate)

can be replaced with:

 privatestaticvoid DoSomething(Expression<Predicate<Mock><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))), newParameterExpression[] { 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 LINQCTP includes a very cool visualizer to see what's in the expression tree once it reaches your method body at runtime:

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:

 publicstaticMethodInfo Method<TDeclaringType>(Expression<Operation> method) { return GetMethodInfo(method); } privatestaticMethodInfo GetMethodInfo(Expression method) { LambdaExpression lambda = method asLambdaExpression; if (lambda == null) thrownewArgumentNullException("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 asMethodCallExpression; } elseif (lambda.Body.NodeType == ExpressionType.MethodCall || lambda.Body.NodeType == ExpressionType.MethodCallVirtual) {  methodExpr = lambda.Body asMethodCallExpression; } if (methodExpr == null) thrownewArgumentException("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:

 publicdelegateobjectOperation(); publicdelegateobjectOperation(T declaringType); publicdelegateobjectOperation(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 disappear in the future, in favor of something more elegant (maybe publicdelegateobject 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.

 publicstaticMethodInfo Method<TDeclaringType>(Expression<Operation<TDeclaringType>> method) { return GetMethodInfo(method); } publicstaticMethodInfo Method<TDeclaringType, A0>(Expression<Operation<TDeclaringType, A0>> method) { return GetMethodInfo(method); } publicstaticMethodInfo 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!

About the author

Daniel Cazzulino (a.k.a. kzu) lives in Buenos Aires, Argentina, and is a senior architect, developer, and cofounder of Clarius Consulting S.A. He has coauthored several books on Web development and server controls with ASP.NET, written and reviewed many articles for ASP Today and C# Today, and currently enjoys sharing his .NET and XML experiences through his blog.

This was first published in January 2008

There are Comments. Add yours.

TIP: Want to include a code block in your comment? Use <pre> or <code> tags around the desired text. Ex: <code>insert code</code>

REGISTER or login:

Forgot Password?
By submitting you agree to receive email from TechTarget and its partners. If you reside outside of the United States, you consent to having your personal data transferred to and processed in the United States. Privacy
Sort by: OldestNewest

Forgot Password?

No problem! Submit your e-mail address below. We'll send you an email containing your password.

Your password has been sent to:

Disclaimer: Our Tips Exchange is a forum for you to share technical advice and expertise with your peers and to learn from other enterprise IT professionals. TechTarget provides the infrastructure to facilitate this sharing of information. However, we cannot guarantee the accuracy or validity of the material submitted. You agree that your use of the Ask The Expert services and your reliance on any questions, answers, information or other materials received through this Web site is at your own risk.