January 6th, 2010

Getting Information About Objects, Types, and Members with Expression Trees

Starting with C# 3.0 and Visual Studio 2008, you can use expression trees to get information about objects, types, and members. In this post I’m going to show some examples and explain what benefits you can get by using this technique. If you are not familiar with expression trees, I would recommend reading Charlie Calvert’s blog post Expression Tree Basics first.

Let’s start with a simple task. Assume that you want to print the name of a field or a property next to its value. For example, imagine that you have the following class.

public class SampleClass { public string SomeField = “Test”; }

Of course, there is a straightforward solution:

SampleClass sample = new SampleClass(); Console.WriteLine(“SomeField : {0}”, sample.SomeField); // Prints SomeField : Test

The problem with the above code is that you use a string literal SomeField. Nothing guarantees that this is the real name of the field. You might type the name incorrectly or just accidentally specify the wrong name. Furthermore, if you rename your property to, say, AnotherField, it could be hard to find a string literal that represents its name.

Here is how you can create a method that returns a property name by using expression trees, without using any string literals:

public static string GetName<T>(Expression<Func<T>> e) { var member = (MemberExpression)e.Body; return member.Member.Name; }

To call this method, you need to give it a lambda expression, as shown below.

Console.WriteLine(“{0} : {1}”, GetName(() => sample.SomeField), sample.SomeField); // Also prints SomeField : Test

Because of the lambda expression, you get not only compile-time error checking, but also full IntelliSense support when typing a member name. And if you rename a property, the compiler will find all the places where the property name is used. Or you can rely on refactoring tools to rename all the instances of the property for you.

In fact, you can also use this method to get the name of the variable itself (which can be convenient for tracing and logging).

Console.WriteLine(“{0} : {1}”, GetName(() => sample), sample); //Prints sample : SampleClass

The only problem with using a lambda expression is that you need to ensure that the user passes the correct one. For example, the user of your method can pass a lambda expression like () => new SampleClass(). Unfortunately, you can’t get a compile check for this. So, when using a lambda expression, make sure that you really get the expression you expect. For example, like this:

public static string GetName<T>(Expression<Func<T>> e) { var member = e.Body as MemberExpression;

// If the method gets a lambda expression // that is not a member access, // for example, () => x + y, an exception is thrown. if (member != null) return member.Member.Name; else throw new ArgumentException( “‘” + e + “‘: is not a valid expression for this method”); }

One more scenario where expression trees can help you is getting information about members of a type. C# provides the typeof operator to get type information, but does not provide operators like memberof or infoof. You can use reflection methods such as Type.GetField(), Type.GetMethod(), and so on, but then you have to use string literals again.

// By using typeof operator, you can get type information // without using string literals. Type type = typeof(SampleClass);

// But to get information about a member by using reflection, // you have to rely on string literals. FieldInfo fieldInfo = type.GetField(“SomeField”);

Console.WriteLine(fieldInfo); // Prints System.String SomeField

The problem gets worse if you have overloaded methods in your class.

public class SampleClass { public string SomeField = “Test”; public void SomeMethod() { } public void SomeMethod(string arg) { } }

In this case, you need to specify a method and also its parameters.

MethodInfo methodInfo = type.GetMethod( “SomeMethod”, new Type[] { typeof(String) }); Console.WriteLine(methodInfo); // Prints Void SomeMethod(System.String)

Now you have more ways to get yourself into trouble. In addition to explicitly specifying the name of your method, you’ve also specified the number and types of its parameters. So any changes in the method signature can affect the behavior of your program, and the compiler will not detect it.

With expression trees you can get the same information without using string literals, and also get the compiler to check whether the member you need exists. Furthermore, all you need to do to get a necessary method overload is provide an example of the method usage within a lambda expression. Thanks to Mads Torgersen, a Visual Studio Program Manager, for providing the following code example.

public static MemberInfo MemberOf<T>(Expression<Func<T>> e) { return MemberOf(e.Body); }

// We need to add this overload to cover scenarios // when a method has a void return type. public static MemberInfo MemberOf(Expression<Action> e) { return MemberOf(e.Body); }

private static MemberInfo MemberOf(Expression body) { { var member = body as MemberExpression; if (member != null) return member.Member; }

{ var method = body as MethodCallExpression; if (method != null) return method.Method; }

throw new ArgumentException( “‘” + body + “‘: not a member access”); }

Now you can use the MemberOf method for all kinds of scenarios, for both instance and static members.

Console.WriteLine(MemberOf(() => sample.SomeField)); // Prints System.String SomeField

// To choose a particular method overload, // you simply show the usage of the method. Console.WriteLine(MemberOf(() => sample.SomeMethod(“Test”))); // Prints Void SomeMethod(System.String)

Console.WriteLine(MemberOf(() => sample.SomeMethod())); // Prints Void SomeMethod()

Console.WriteLine(MemberOf(() => Console.Out)); // Prints System.IO.TextWriter Out

I want to repeat that this functionality is already available in C# 3.0 and .NET Framework 3.5. If you want to know how expression trees are extended in .NET Framework 4, take a look at one of my previous posts: Generating Dynamic Methods with Expression Trees in Visual Studio 2010.

Update

See also a follow-up post: How can I get objects and property values from expression trees?

Author

Visual Studio has evolved from a simple tool bundle into an intelligent, all-in-one development environment. With support for coding in any language on any device, integrated AI to streamline workflows, and seamless cloud scalability, it empowers developers to innovate, deliver faster, and build the future.

0 comments

Discussion are closed.