【问题标题】:Is there a way to set 'DeclaringType' in an expression tree?有没有办法在表达式树中设置“DeclaringType”?
【发布时间】:2015-12-14 22:09:29
【问题描述】:

我正在做 Func -> Expression -> Func 转换。如果我从方法(下面的第一个示例)创建 Func() 它工作正常但是如果我使用表达式树创建函数(第二个示例)它在访问 时失败并出现 NullReferenceException >func2.Method.DeclaringType.FullName。这是因为 DeclaringType 为空。 (NJection 使用反射,所以我认为这就是它需要 DeclaringType 的原因。)

如何为通过编译表达式树创建的 Func 填写 DeclaringType 类型? (也许不可能?)DeclaringType 在第一个示例中设置。

从方法中使用 Func...(效果很好)

// Build a Func<> 
Func<int, int> add = Add;
// Convert it to an Expression using NJection Library
Expression<Func<int, int>> expr = ToExpr<Func<int, int>>(add);
// Convert it back to a Func<>
Func < int, int> func = expr.Compile();
// Run the Func<>
int result = func(100);

使用表达式树(不起作用)...

// Build a Func<> using an Expression Tree
ParameterExpression numParam = Expression.Parameter(typeof(int));
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression numAddFive = Expression.Add(numParam, five);
Func<int, int> func2 = 
    Expression.Lambda<Func<int, int>>(
        numAddFive, 
        new ParameterExpression[] { numParam }).Compile();
// Convert to an Expression using NJection (EXCEPTION ON NEXT LINE)
// ToExpr is trying to access func2.Method.DeclaringType.FullName(DeclaringType is null)
Expression<Func<int, int>> exp2 = ToExpr<Func<int, int>>(func2);
// Convert it back to a Func<>
Func<int, int> func2b = exp2.Compile();
// Run the Func<>
int result2 = func2b(100);

【问题讨论】:

  • 你想用ToExpr&lt;Func&lt;int, int&gt;&gt;(func2) 达到什么目的?你期待什么样的Expression回来?
  • 我还在学习 linq/labda/etc。我的目标是简化表达式表达式程序(或树)。也许我可以让编译器将表达式编译为代码,然后获取该代码并将其转换回表达式树。我猜想得到的表达式树将是原始的简化版本。因此,如果我有一个 a+a+a+2+4+3 的表达式树,那么我编译它并将该代码转换回我会得到 (3*a)+9 的表达式。 (我猜)我说的是 Func -> Expr -> Fun,但真正的目标是 Expr -> Func(code) -> Expr。如果我能做到第一个,那么我想我可以做到后一个。
  • 为什么要简化表达式?如果您只是在之后编译并运行,编译器仍然会对其进行所有优化,所以不要打扰。如果您尝试使用编译器为您简化其他任务的表达式 - 您可能不会取得太大成就,因为您对“简单”的想法可能与编译器的想法完全不同。
  • 两个原因:这是一个科学项目。 =) 我计划生成随机函数,然后稍微修改这些函数以创建一个新函数。加班会有很多垃圾,并且可以不时地进行简化以保持这些垃圾干净,因此希望编译得更快。 (类似于遗传算法)第二个原因是,如果有一种方法可以使用 C# 编译器的优化能力来简化任何块表达式,那就太好了!
  • 您知道,C# 编译器几乎没有优化。主要部分由 JIT 完成(即在将 IL 转换为本机代码期间),因此像 NJection 这样的库对您没有帮助......

标签: c# lambda expression-trees linq-expressions


【解决方案1】:

我不知道 NJection 库的用途是什么,因为他们的网站已关闭,而且他们的 Codeplex 上没有可用的源代码。

如果您只需要获得一个可以编译回函数的Expression,您可以自己创建它。例如。像这样:

static Expression<T> ToExpr<T>(T func)
{
    var type = typeof(T);
    var method = type.GetMethod("Invoke"); // Delegate.Invoke() has parameters types matching delegate parameters types
    var pars = method.GetParameters()
        .Select(pi => Expression.Parameter(pi.ParameterType))
        .ToArray();

    return Expression.Lambda<T>(
        Expression.Call(Expression.Constant(func), method, pars),
        pars
    );
}

有了这个ToExpr,你上面的代码就可以毫无问题地编译和运行了。

【讨论】:

  • 图书馆在这里github.com/sagifogel/NJection.LambdaConverter。我复制了“ExecuteLambda”方法,然后删除了最终的“.compile()”并将其保存为“ToExpr”方法。您的方法适用于我的示例代码,但我注意到“expr.body”有一个“调用”而不是一个“块”,其中包含像“NJection”产生的表达式。我可能不知道我在说什么。 =) 你创建了一个适用于测试数据的函数真是太酷了!
  • 我仍然不确定 NJection 是做什么的,但如果它真的反编译了委托,那么我的回答对你没有帮助。此外,为了简化表达式树,您可以使用 relinq 的 PartialEvaluatingExpressionTreeVisitor。它可能不会为您提供a+a+a -> 3*a 转换,但它肯定会将2+4+3 转换为9
  • 他们有一个网站,大约一个月前就上线了,但看起来他们的老爸域名已过期。它基本上是一个允许将表达式保存为 xml 的工具,但它有一个可以将 func 转换为 expr 的函数。我查看了 relinq,但找不到 PartialEvaluatingExpressionTreeVisitor 的任何代码示例。我会尝试使用它。感谢您的想法和所有帮助。它帮助了我。
  • 它的作用 - 它只评估每个可以评估的子树(即不包含参数)。您只需使用您的Expression 调用它的静态EvaluateIndependentSubtrees 方法,它会将简化的Expression 返回给您。顺便说一句,您不必下载整个库 - 您可以获得 source of this class from github + 几个相邻的类并使用它们(并最终 - 根据您的需要进行修改)。
【解决方案2】:

我猜没有。

当您将表达式编译为可执行的 Func 时,从技术上讲,它是一种动态方法。如果你打电话给func2.Method.GetType(),你可以看到这个。这将返回type DynamicMethod,它始终将null 作为Declaring Type

【讨论】:

    猜你喜欢
    • 2014-08-25
    • 1970-01-01
    • 1970-01-01
    • 2022-06-29
    • 1970-01-01
    • 2016-02-12
    • 1970-01-01
    • 1970-01-01
    • 2022-07-19
    相关资源
    最近更新 更多