c# - 构建表达式树抛出 NotSupportedException "TryExpression is not supported as an argument to method.."

标签 c# lambda expression-trees

我正在尝试构建一个表达式树,但它在编译时抛出 NotSupportedException:

TryExpression is not supported as an argument to method 'Boolean TryGetMemberValue(System.Object, System.String, System.Object ByRef)' because it has an argument with by-ref type. Construct the tree so the TryExpression is not nested inside of this expression.

我有点明白它说的是什么,但我不知道我需要改变什么......这是代码:

static void Main()
{
    //A.Child.Name
    var child = CreateExpression("Child", proxyParameter);
    var name = CreateExpression("Name", child);

    var expr = Expression.Lambda<Func<Proxy, object>>(name, proxyParameter);

    var func = expr.Compile();
}

class A
{
    public A Child { get; set; }
    public string Name { get; set; }
}

abstract class Proxy
{
    public abstract bool TryGetMemberValue(object parent, string name, out object result);
}

static ParameterExpression proxyParameter = Expression.Parameter(typeof(Proxy), "proxy");

static Expression CreateExpression(string propertyName, Expression parent)
{
    var tryGetMemberMethod = typeof(Proxy).GetMethod("TryGetMemberValue");

    var result = Expression.Variable(typeof(object), "out result");

    var returnTarget = Expression.Label(typeof(object));

    var tryGetMember =
        Expression.Block(
            new[] { result },
            Expression.IfThenElse(Expression.Equal(Expression.Call(proxyParameter, tryGetMemberMethod, parent, Expression.Constant(propertyName), result), Expression.Constant(true)),
                Expression.Return(returnTarget, result),
                Expression.Throw(Expression.Constant(new MissingMemberException(propertyName)))),
            Expression.Label(returnTarget, Expression.Constant(null)));

    return tryGetMember;
}

生成以下表达式:

.Lambda #Lambda1<System.Func`2[Program+Proxy,System.Object]>(Program+Proxy $proxy) {
.Block(System.Object $'out result') {
    .If (.Call $proxy.TryGetMemberValue(
        .Block(System.Object $'out result') {
            .If (.Call $proxy.TryGetMemberValue(
                $proxy,
                "Child",
                $'out result') == True) {
                .Return #Label1 { $'out result' }
            } .Else {
                .Throw .Constant<System.MissingMemberException>(System.MissingMemberException: Child)
            };
            .Label
                null
            .LabelTarget #Label1:
        },
        "Name",
        $'out result') == True) {
        .Return #Label2 { $'out result' }
    } .Else {
        .Throw .Constant<System.MissingMemberException>(System.MissingMemberException: Name)
    };
    .Label
        null
    .LabelTarget #Label2:
}

有什么想法吗?

/* 已编辑 */ 有趣的是,这工作得很好:

var tryGetMember =
    Expression.Block(
        new[] { result },
        Expression.Call(proxyParameter, tryGetMemberMethod, parent, Expression.Constant(propertyName), result),
    result);

最佳答案

虽然异常消息还不清楚,但我认为您刚刚遇到了 Expression 编译器的限制:在带有 ref 的方法的调用表达式下不能有复杂的子表达式 参数。

我认为解决这个问题的方法基本上与在 C# 代码中执行的操作相同:使用局部变量来保留中间结果:

var child = Expression.Variable(typeof(object), "child");

var body = Expression.Block(
    new[] { child },
    Expression.Assign(child, CreateExpression("Child", proxyParameter)),
    CreateExpression("Name", child));

var expr = Expression.Lambda<Func<Proxy, object>>(body, proxyParameter);

通过此更改,代码不再引发异常。

此外,您不需要在 tryGetMember 表达式中使用 LabelReturn。相反,您可以将 IfThenElse 替换为 Condition(基本上是 C# 中的三元运算符)。如果这样做,您还需要指定 Throw 的类型(即使它从未实际返回):

var tryGetMember =
    Expression.Block(
        new[] { result },
        Expression.Condition(
            Expression.Equal(
                Expression.Call(
                    proxyParameter, tryGetMemberMethod, parent,
                    Expression.Constant(propertyName), result),
                Expression.Constant(true)),
            result,
            Expression.Throw(
                Expression.Constant(new MissingMemberException(propertyName)),
                typeof(object))));

关于c# - 构建表达式树抛出 NotSupportedException "TryExpression is not supported as an argument to method..",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19625232/

相关文章:

c# - 使用 Reflection Invoke 静态泛型方法将 Lamba 作为参数传递

c# - 也许 monad 使用表达式树?

c# - 表达式树

c# - FFMPEG 将音轨连接到更长的视频轨道时,音频循环

c# - Exchange Web 服务附件集合为空

c# - 用 C# 开发应用程序的最佳第 3 方 GUI 框架是什么?

c# - C# 中泛型接口(interface)的简写语法

java - 如何从 Set/Map 中删除多个元素并知道哪些元素被删除了?

c++ - GoogleTest Framework 似乎不适用于 Lambda 函数(跟进)

c# - 如何用表达式树表达 All have Any