更改表达式的参数值<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"
假设我有 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"