gpt4 book ai didi

c# - 如何在二进制表达式中将参数表达式更改为常量表达式

转载 作者:行者123 更新时间:2023-11-30 20:26:40 24 4
gpt4 key购买 nike

我有我的自定义访问者,它向右和向左看,并将参数更改为常量。
我知道只改变节点是不可能的。
我应该返回包含常量而不是参数的新 lambda 表达式。但我不能自己创建一个表达式:(
我有这段代码:

    public class ParametersTransformToConstantVisitor : ExpressionVisitor
{

private Dictionary<string, ConstantExpression> parameters = new Dictionary<string, ConstantExpression>();

public ParametersTransformToConstantVisitor(Dictionary<string, ConstantExpression> parameters)
{
this.parameters = parameters;
}

protected override Expression VisitBinary(BinaryExpression node)
{
var constExprLeftName = new Lazy<string>(() => ((ParameterExpression) node.Left)?.Name);
var constExprRightName = new Lazy<string>(() => ((ParameterExpression) node.Right)?.Name);
var constExprName = new Lazy<string>(() => ((ParameterExpression) node.Reduce())?.Name);

ParameterExpression leftParam = null;
ParameterExpression rightParam = null;

if (node.NodeType == ExpressionType.Parameter && parameters.ContainsKey(constExprName.Value))
{
return parameters[constExprName.Value];
}

if (node.Left.NodeType == ExpressionType.Parameter && parameters.ContainsKey(constExprLeftName.Value))
{
leftParam = (ParameterExpression) node.Left;
}

if (node.Right.NodeType == ExpressionType.Parameter && parameters.ContainsKey(constExprLeftName.Value))
{
rightParam = (ParameterExpression) node.Right;
}

if (leftParam != null || rightParam != null)
{
//return Expression.Lambda();
}

return base.VisitBinary(node);
}
}

请帮助我构建 lambda 表达式

最佳答案

感觉您真正需要的是:

protected override Expression VisitParameter(ParameterExpression node)
=> parameters.TryGetValue(node.Name, out var ce) ? (Expression)ce : node;

protected override Expression VisitLambda<T>(Expression<T> node)
=> Expression.Lambda(Visit(node.Body), node.Parameters); // don't visit the parameters

即每当访问者看到 ParameterExpression 时,如果 parameters 映射中有相应的项目,则使用该值。

覆盖 VisitLambda 是因为 VisitLambda 仍然需要返回一个相同形状的 lambda ,并且默认实现也会访问 (从而从声明中换出参数

访问者的工作是担心围绕您的更改重新组装树。

但是请注意,如果您尝试创建一个无参数 lambda,您可能还需要重写根。或者您可以只使用 .Body 而忽略参数。

例子:

Expression<Func<int, int, string>> add = (x, y) => ((2 * x) + y).ToString();
Console.WriteLine(add);

var args = new Dictionary<string, ConstantExpression>
{
["x"] = Expression.Constant(4),
["y"] = Expression.Constant(1),
};

var visitor = new ParametersTransformToConstantVisitor(args);
var result = (LambdaExpression)visitor.Visit(add);
Console.WriteLine(result);

给出:

(x, y) => ((2 * x) + y).ToString()
(x, y) => ((2 * 4) + 1).ToString()

您可以通过以下方式将其变成无参数的 lambda:

var withoutArgs = Expression.Lambda<Func<string>>(result.Body);
Console.WriteLine(withoutArgs);

给出:

() => ((2 * 4) + 1).ToString()

次要添加:您可能还想简化访问者:

protected override Expression VisitBinary(BinaryExpression node)
{
var visited = base.VisitBinary(node);

if(visited is BinaryExpression be
&& be.Method == null && be.Conversion == null
&& !be.IsLifted
&& be.Left is ConstantExpression left
&& be.Right is ConstantExpression right)
{

object val;
switch(be.NodeType)
{
case ExpressionType.Add:
val = (dynamic)left.Value + (dynamic)right.Value;
break;
case ExpressionType.Multiply:
val = (dynamic)left.Value * (dynamic)right.Value;
break;
case ExpressionType.Subtract:
val = (dynamic)left.Value - (dynamic)right.Value;
break;
case ExpressionType.Divide:
val = (dynamic)left.Value / (dynamic)right.Value;
break;
default:
return visited; // unknown
}

return Expression.Constant(
Convert.ChangeType(val, visited.Type), visited.Type);
}
return visited;
}

这会将输出更改为:

(x, y) => ((2 * x) + y).ToString()
(x, y) => 9.ToString()
() => 9.ToString()

而且我们可能甚至可以提升ToString()!

protected override Expression VisitMethodCall(MethodCallExpression node)
{
var visited = base.VisitMethodCall(node);
if (visited is MethodCallExpression mce)
{
if ((mce.Object == null || mce.Object is ConstantExpression)
&& mce.Arguments.All(x => x is ConstantExpression))
{
var obj = (mce.Object as ConstantExpression)?.Value;
var args = mce.Arguments.Select(x => ((ConstantExpression)x).Value).ToArray();
var result = mce.Method.Invoke(obj, args);
return Expression.Constant(result, mce.Type);
}
}
return visited;
}

现在给我们:

(x, y) => ((2 * x) + y).ToString()
(x, y) => "9"
() => "9"

关于c# - 如何在二进制表达式中将参数表达式更改为常量表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49530146/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com