Using Expression Trees to Avoid String Literal References
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
- Artificial Intelligence
- Cloud Services
- Design
- Development
- Digital Health
- Digital Marketing
- Digital Transformation
- Environment
- Life at Storm
- UX
Archives
Subscribe to Email Updates
Subscribe
Categories
- Artificial Intelligence
- Cloud Services
- Design
- Development
- Digital Health
- Digital Marketing
- Digital Transformation
- Environment
- Life at Storm
- UX
Archives
Subscribe to Email Updates
Subscribe
- Artificial Intelligence
- Cloud Services
- Design
- Development
- Digital Health
- Digital Marketing
- Digital Transformation
- Environment
- Life at Storm
- UX
Archives
Subscribe to Email Updates
SubscribeWe are a digital transformation consultancy. We help our clients succeed.
View Services