在表达式树中的动态方法调用

3
构建表达式树时,我需要使用调用外部方法的节点来获取表达式可以继续评估的值。这些方法被提供为Func<T>,我的代码不知道它们来自哪里。
执行上述调用的最正确的方法是什么?我尝试过类似这样的方法:
private Dictionary<string, Delegate> _externalSymbols;

private Expression _forExternalSymbol(string identifier)
{
    Delegate method = _externalSymbols[identifier];
    return Expression.Call(method.Method);
}

只要从字典获取的方法是在编译时创建的,那么它就可以正常工作。但是,在动态方法Func<T>情况下,例如通过在运行时编译另一个表达式获得,这将不起作用,会抛出以下异常:

ArgumentException: 调用方法“Int32 lambda_method(System.Runtime.CompilerServices.ExecutionScope)”时提供了错误数量的参数

将给定函数包装在一个额外的表达式中可能实现期望的效果,但与原来的代码相比,这似乎相当丑陋。
private Expression _forExternalSymbol(string identifier)
{
    Delegate method = _externalSymbols[identifier];
    Expression mediator = method is Func<double> ?
        (Expression)(Expression<Func<double>>)(() => ((Func<double>)method)()) :
        (Expression<Func<string>>)(() => ((Func<string>)method)());
    return Expression.Invoke(mediator);
}

此外,这种方法很难扩展,如果我需要支持除doublestring之外的其他类型。

我想知道是否有更好的选项可以与动态创建的方法配合使用(最好适用于.NET 3.5)。


参数呢?这些方法需要任何参数吗,还是不需要? - nightwatch
@nightwatch 没有参数,字典保证只包含 Func<T> 委托,其中 T 可以是 doublestring(这些类型的列表可能需要在将来进行扩展)。 - Denis Aldoshin
我不太熟悉linq表达式,抱歉。但是你正在做的看起来像是在运行时解析方法名的动态调用。也许你可以只使用一个动态对象作为接口来调用外部函数? - nightwatch
1个回答

2
只要从字典中获取的method是在编译时创建的,这个方法就有效。
不,只要method是静态的就有效。例如,如果委托是引用其父作用域中的某些内容(即它是闭包),则它也无法工作。
调用委托的正确方式是使用Expression.Invoke()。要获取表示您的委托的Expression,请使用Expression.Constant()
Expression.Invoke(Expression.Constant(method)))

好的,实际上它适用于非静态方法(在这种情况下,必须使用重载版本,并将 Expression.Constant(method.Target) 作为第一个参数),问题是动态方法没有 Target。您的答案正是我所需要的,谢谢! - Denis Aldoshin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接