Using Expression Trees to Avoid String Literal References

30 June 2009
 

Often a developer will want to mirror the names of properties or methods of a class in some related dynamic resource that needs to bind to named members of the class. This happens a lot in user interface and serialization scenarios.

For example, a user interface may need to populate the value of a text-box with a value dynamically looked up from a property on an instance of a class. Typically, this is done with a hard-coded string literal.

To avoid using hard-coded member names, expression trees can be used to define members, then utility code can translate that tree to a string. The advantage here is that refactoring is pretty much guaranteed to work, and you get compile time checking.

So, lets say I have the following class, and I am interested in writing the the names of the members to the console.

class MyType
{
    public string AStringProperty { get; set; }

    public object GetAnObject()
    {
        return new object();
    }

    public int GetAnInt(string a1, string a2)
    {
        return 0;
    }
}

With a small amount of utility code, I can achieve my goal without string literals or resorting to reflection.

Console.WriteLine(Name.Of(x => x.AStringProperty)); 
Console.WriteLine(Name.Of(x => x.GetAnObject())); 
Console.WriteLine(Name.Of(x => x.GetAnInt(null, null)));

Which results in console output:

AStringProperty 
GetAnObject 
GetAnInt 

Utility Code The Name class has a single static method Of, which uses another the ExpressionFinder class to work out the member or method.

class Name
{
    public static string Of(Expression> expr)
    {
        return ExpressionMemberFinder.FindMemberOrNull(expr).Name;
    }
}

class ExpressionMemberFinder
{
    public static MemberInfo FindMemberOrNull(Expression expression)
    {
        switch (expression.NodeType)
        {
            case ExpressionType.Convert:
                return FindMemberOrNull(((UnaryExpression) expression).Operand);
            case ExpressionType.Lambda:
                return FindMemberOrNull(((LambdaExpression) expression).Body);
            case ExpressionType.Call:
                return ((MethodCallExpression) expression).Method;
            case ExpressionType.MemberAccess:
                return ((MemberExpression) expression).Member;
            default:
                return null;
        }
    }
}

It’s worth noting that the only type of expression tree we can work on is one that ends in a single method-call or member-access. It doesn’t make any sense to work on a tree with branches or multiple leaf nodes.

Lets examine what happens in the FindMemberOrNull method by looking at three kinds of tree that we can handle.

Behaviour #1, Finding a Property Access

Name.Of<MyType>(x => x.AStringProperty));
FindMemberOrNull is called with a lambda {x => x.AStringProperty}
FindMemberOrNull is called from within itself with the lambda’s body, a member-expression {x => x.AStringProperty}
The member-expression’s member is returned

Behaviour #2, Finding a Method That Returns an Object

Name.Of<MyType>(x => x.GetAnObject()));
FindMemberOrNull is called with a lambda {x => x.GetAnObject() }
FindMemberOrNull is called from within itself with the lambda’s body, a method-call-expression {x => x.GetAnObject()}
The method-call-expression’s method is returned

Behaviour #3, Finding a Method That Returns an Something Other Than an Object

Name.Of<MyType>(x.GetAnInt(null, null))); Slightly more interesting behaviour here.
As the FindMemberOrNull method accepts a expression describing a function that returns System.Object, the compiler automatically wraps the expression in a unary-expression which converts from Expression<Func<MyType, int>> to Expression<Func<MyType, object>>. NICE!
FindMemberOrNull is called with a lambda {x => Convert(x.GetAnInt(null, null))}
FindMemberOrNull is called from within itself with the lambda’s body, a unary-expression {x =>Convert(x.GetAnInt(null, null))}
FindMemberOrNull is called from within itself with the unary-expression’s operand, a method-call-expression {x => x.GetAnInt(null, null)}
The method-call-expression’s method is returned

John McDowall

Search

Categories

Archives

Subscribe to Email Updates

Subscribe
 

Comments are closed.

We are a digital transformation consultancy. We help our clients succeed.

View Services