Search code examples
c#lambdaexpression-trees

MemberExpression.ToString() - substituting parameters


Given this method:

public static string GetMemberIdentifier<TProp>(Expression<Func<Team, TProp>> expression)
{
    return expression.Body.ToString();
}

A lambda expression can be converted to a string representation:

var nameIdentifier1 = GetMemberIdentifier(t => t.Members[1].Name);

which gives (a):

t.Members.get_Item(1).Name

However calling it with:

var i = 0;
var nameIdentifier2 = GetMemberIdentifier(t => t.Members[i].Name);

results in (b):

t.Members.get_Item(value(Program+<>c__DisplayClass2).i).Name

Is it possible to get the output in b) to be the same as in a) ?

.NET Fiddle: https://dotnetfiddle.net/cHG7H3

Desired Usage(Simpified):

for(int i = 0; i < team.Members.Count; i++)
{
    Console.WriteLine(GetMemberIdentifier(t => t.Members[i].Name));
}

Output:

t.Members.get_Item(0).Name
t.Members.get_Item(1).Name
t.Members.get_Item(2).Name

Solution

  • If your lambdas are going to be this simple, then you can do:

    public class ArgumentsTransformToConstantVisitor : ExpressionVisitor
    {
        private readonly Dictionary<string, Func<object>> args;
    
        public ArgumentsTransformToConstantVisitor(params (string varname, Func<object> resolver)[] args)
        {
            this.args = args.ToDictionary(i => i.varname, i => i.resolver);
        }
        
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Expression is ConstantExpression && this.args.TryGetValue(node.Member.Name, out Func<object> resolver))
                return Expression.Constant(resolver());
            
            return base.VisitMember(node);
        }
    
        public string GetMemberIdentifier<TProp>(Expression<Func<Team, TProp>> expression)
        {
            return ((LambdaExpression)this.Visit(expression)).ToString();
        }
    }
    
    int i = 0;
    
    var visitor = new ArgumentsTransformToConstantVisitor(("i", () => i));
    
    for (i = 0; i < 5; i++)
    {
        Console.WriteLine(visitor.GetMemberIdentifier(t => t.Members[i].Name));
    }