Type Member Name Resolution with Functions – Part 2.0
Adding to the previous post, the PropertyNameResolver class needs to expand it’s horizons and resolve method names and field names. Given the expanded requirement we probably need to refactor the name of the class to something more appropriate. Here is a more fleshed out idea of what it might look like:
public class MemberNameResolver<T> { public static string ResolveProperty<TPropertyType> (Expression<Func<T, TPropertyType>> expression) { Func<Expression<Func<T, TPropertyType>>, string> prop_name_function = l => { return GetNameFromExpression(l.Body); }; var property_name = typeof(T).GetProperty(prop_name_function(expression)).Name; return property_name; } public static string ResolveMethod(Expression<Action<T>> expression) { Func<Expression<Action<T>>, string> prop_name_function = l => { return GetNameFromExpression(l.Body); }; var property_name = typeof(T).GetMethod(prop_name_function(expression)).Name; return property_name; } public static string ResolveField<TPropertyType> (Expression<Func<T, TPropertyType>> expression) { Func<Expression<Func<T, TPropertyType>>, string> prop_name_function = l => { return GetNameFromExpression(l.Body); }; var property_name = typeof(T).GetField(prop_name_function(expression)).Name; return property_name; } private static string GetNameFromExpression(Expression body) { if (ExpressionType.MemberAccess == body.NodeType) { var memberExpr = (MemberExpression)body; return memberExpr.Member.Name; } else if (ExpressionType.Call == body.NodeType) { var methodExpr = (MethodCallExpression)body; return methodExpr.Method.Name; } else { throw new NotImplementedException( string.Format(“Resolving names from expression types “ + “of {0} is not implemented!”, body.Type.Name)); } } }
Notice the newly specialized Resolve methods now allow a usage like the following:
var member1 = MemberNameResolver<Criteria> .ResolveProperty<int>(c => c.PageCounter); var member2 = MemberNameResolver<Criteria> .ResolveMethod(c => c.SomeMethod()); var member3 = MemberNameResolver<Criteria> .ResolveField(c => c.Percent); var member4 = MemberNameResolver<Criteria> .ResolveMethod(c => c.SomeMethodWithArgs(default(string), default(int), default(string[]))); Console.WriteLine(member1); Console.WriteLine(member2); Console.WriteLine(member3); Console.WriteLine(member4);
Producing the following output:
Getting more out of Functions
So far the approach is a little static from a functional point of view so perhaps we could do this a little differently? If we define the following functions we get some equivalent behaviour to our utility class.
Func<Expression<Func<Criteria, int>>, Expression> criteriaFunction = c => c.Body; Func<Expression, string> propertyResolverFunction = e => { if (ExpressionType.MemberAccess == e.NodeType) { var memberExpr = (MemberExpression)e; return memberExpr.Member.Name; } else if (ExpressionType.Call == e.NodeType) { var methodExpr = (MethodCallExpression)e; return methodExpr.Method.Name; } else { throw new NotImplementedException( string.Format(“Resolving names from expression types “ + “of {0} is not implemented!”, e.Type.Name)); } }; var propertyName = propertyResolverFunction( criteriaFunction(c => c.PageCounter)); Console.WriteLine(propertyName);
Here we have created two functions that can be used together to achieve our required end result. The first function criteriaFunction, takes a generic expression as an argument and returns an expression. The second function propertyResolverFunction, takes an expression as it’s argument and returns a string. By passing one function as an argument to the other, we achieve the same results as with the static utility class already demonstrated. To collapse this even further we can wrap up both those functions into one.
Func<Expression<Func<Criteria, string>>, Func<Expression, string>> allInOne = c => e => { if (ExpressionType.MemberAccess == e.NodeType) { var memberExpr = (MemberExpression)e; return memberExpr.Member.Name; } else if (ExpressionType.Call == e.NodeType) { var methodExpr = (MethodCallExpression)e; return methodExpr.Method.Name; } else { throw new NotImplementedException( string.Format(“Resolving names from expression types “ + “of {0} is not implemented!”, e.Type.Name)); } }; Expression<Func<Criteria, string>> exp = c => c.Levels; var property_name_allinone = allInOne(exp)(exp.Body); Console.WriteLine(property_name_allinone);
When we call the allinone function with the expression argument, it returns a function that in turns take the body property of the same expression that was passed to the higher order function. All the code in the post presumes the the following class:
public class Criteria { public decimal Percent; private int _pageCounter; public int PageCounter { get { return _pageCounter; } set { _pageCounter = value; } } private string _levels; public string Levels { get { return _levels; } set { _levels = value; } } public void SomeMethod() { } public void SomeMethodWithArgs(string arg1, int arg2, params string[] arg3) { } }
1 Comment so far
Leave a reply









[...] Simon Segal shows an approach to be used in resolving a types property name using strongly typed expressions. [...]