Search code examples
c#.netexpressionexpression-treesfunc

Change Parameter Value of Expression<Func<<string>>


Let's say I've got the class Item which looks like this

public class Item
{
    // ..
    Expression<Func<string>> Callback {get; set;}
}

Item defines a property called Callback which can be used like this

public string TestFunction(string ident, DateTime value1, DateTime value2)
{
    return string.Join(";", ident, value1, value2);
}

// ..

Item x = new Item();

x.Callback = () => TestFunction("Hello there", DateTime.Now.Date, DateTime.Now);

Console.WriteLine(x.Callback.Compile().Invoke()); // prints the expected output

That works just well. Now, what I'm trying to do is change the value of the DateTime parameters.

I've already figured out how to get the arguments:

MethodCallExpression body = (MethodCallExpression)x.Callback.Body;

foreach(ConstantExpression arg in body.Arguments) {
    if(arg.Type == typeof(DateTime)) {
        //arg.Value =  => READONLY!
    }
}

However, I can't assign a new value to arg.Value because doesn't have a setter.

There seems to be something called ExpressionVisitor but I'm unsure if that's something I need.

Is there any way to achieve what I'm trying to do?

Thank you in advance

__

Update

I almost got it working with @Guru Stron help but there's still a small problem.

This piece of code works perfectly fine:

var newParams = new[] { Expression.Constant("testIdent"), Expression.Constant(DateTime.Now), Expression.Constant(DateTime.Now) };

However, the following code throws an

Expression of type 'System.Linq.Expressions.ConstantExpression' cannot be used for parameter of type 'System.String' of method 'System.String TestFunction(System.String, System.DateTime, System.DateTime)'

Exception.

List<ConstantExpression> para = new List<ConstantExpression>();

foreach (var arg in body.Arguments) {
    if (arg.Type == typeof(DateTime)) {
        para.Add(Expression.Constant(DateTime.Now));
        continue;
    }

    para.Add(Expression.Constant(arg));
}

var exprBody = Expression.Call(body.Object, body.Method, para); // Exception is thrown here

The error is pretty obvious but I can't seem to find a way to convert the parameter to the correct type.

The reason why I changed the code is because I don't know the amount of parameters, so I tried to loop through them any only change the ones I need since the order remains correct.

Any ideas?


Solution

  • You will need to build a new expression and pass new desired parameters to it:

    MethodCallExpression body = (MethodCallExpression)x.Callback.Body;
    var newParams = new[] { Expression.Constant("NEW "), Expression.Constant(DateTime.Now), Expression.Constant(DateTime.Now)};
    var exprBody = Expression.Call(body.Object, body.Method, newParams );
    var newExpr = Expression.Lambda<Func<string>>(exprBody);
    var newFunc = newExpr.Compile();
    Console.WriteLine(newFunc()); // "NEW ;03-Jun-20 5:07:16 PM;03-Jun-20 5:07:16 PM"