更改表达式的参数值<Func<<string>>

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

假设我有 class 看起来像这样的项目

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

Item 定义了一个叫做 Callback 的 属性 可以这样使用

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

效果很好。现在,我要做的是更改 DateTime 参数的值。

我已经知道如何获取参数了:

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

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

但是,我无法为 arg.Value 分配新值,因为没有 setter。

似乎有一个叫做 ExpressionVisitor 的东西,但我不确定这是否是我需要的东西。

有什么方法可以实现我想要做的事情吗?

提前致谢

__

更新

在@Guru Stron 的帮助下,我几乎可以使用它,但仍然存在一个小问题。

这段代码工作得很好:

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

但是,下面的代码抛出一个

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)'

异常。

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

错误很明显,但我似乎找不到将参数转换为正确类型的方法。

我更改代码的原因是因为我不知道参数的数量,所以我尝试遍历它们,只更改我需要的参数,因为顺序保持正确。

有什么想法吗?

您将需要构建一个新表达式并向其传递新的所需参数:

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"