如何通过表达式树生成的委托调用自己?

Posted

技术标签:

【中文标题】如何通过表达式树生成的委托调用自己?【英文标题】:How do I call myself through the delegate generated by the expression tree? 【发布时间】:2019-06-09 04:31:56 【问题描述】:

现在我要把表达式树编译成一个委托来动态生成代码,但是我有一个问题。我必须调用表达式树中的一个方法,这正是尚未动态编译的表达式树委托。我该怎么办?

我想从表达式树生成如下代码:

int i = 0;
Action ac = null;

ac = () =>

    //if (i-- > 0)  condition
        ac();
;

以下代码不起作用,会提示ac is null

static Action ac = Build();
static Action Build()

    return Expression.Lambda<Action>(
        Expression.Call(
            Expression.Constant(ac), //throw ac is null
            typeof(Action).GetType().GetMethod("Invoke")
        )
    ).Compile();

【问题讨论】:

它不起作用吗? 如果你想要一个递归委托,声明一个。不要费心尝试制作无名递归委托。 @J. van Langen 我已经编辑了问题 @user2864740 我已经编辑了问题 我在这里找到了:Recursive Methods in Expression Trees。您必须创建两个 lambda 表达式。第一个生成第二个 lambda 并将其存储在一个变量中,然后将该变量作为参数传递给第二个 lambda。 【参考方案1】:

表达式的问题是你只能按值传递变量,所以你需要一些技巧来传递引用。你可以这样做:

Action<Node> ac = null;
Func<Action<Node>> getAction = () => ac;

然后像这样构建表达式:

ac = () =>

    //if (i-- > 0)  condition
        getAction()();
;

另一种选择是将动作包装在某个对象中:

Wrapper<Action> = new Wrapper();
ac = () =>

    //if (i-- > 0)  condition
        wrapper.Value();
;
wrapper.Value = ac;

这里是示例代码:

    class Wrapper<T>
    
        public T Value  get; set; 
    

    static void Main(string[] args)
    
        Node root = new Node
        
            Name = "First",
            Next = new Node Name = "Second"
        ;

        var method = Build();
        method(root);
    

    class Node
    
        public string Name  get; set; 
        public Node Next  get; set; 
    

    static Action<Node> Build()
    
        var wrapper = new Wrapper<Action<Node>>();
        var param = Expression.Parameter(typeof(Node), "node");
        var expr = Expression.Lambda<Action<Node>>(
            Expression.Block(
                // Console.WriteLine("Node name: 0", node.Name);
                Expression.Call(
                    typeof(Console), 
                    "WriteLine", 
                    Type.EmptyTypes, 
                    Expression.Constant("Node name: 0"), 
                    Expression.Property(param, "Name")
                ),
                // if (node.Next != null) wrapper.Value(node.Next)
                Expression.IfThen(
                    Expression.ReferenceNotEqual(Expression.Property(param, "Next"), Expression.Constant(null)),
                    // wrapper.Value(node.Next)
                    Expression.Invoke(
                        // wrapper.Value
                        Expression.Property(Expression.Constant(wrapper), "Value"),
                        // node.Next
                        Expression.Property(param, "Next")
                    )
                )
            ),
            param
        );

        return wrapper.Value = expr.Compile();
    

【讨论】:

以上是关于如何通过表达式树生成的委托调用自己?的主要内容,如果未能解决你的问题,请参考以下文章

表达式树与委托

自己动手写编译器:通过语法编译构建语法树并实现中间代码生成

表达式树

如何遍历yacc生成的解析树?

C# 表达式树 创建生成使用lambda转成表达式树~表达式树的知识详解

自己动手写ORM(01):解析表达式树生成Sql碎片