【问题标题】:How to create an Expression<Func<dynamic, dynamic>> - Or is it a bug?如何创建一个 Expression<Func<dynamic, dynamic>> - 或者它是一个错误?
【发布时间】:2023-03-05 06:03:02
【问题描述】:

在我这几天使用表达式树的过程中,我遇到了一些我觉得很难理解的东西;希望有人能够在这里有所启发。

如果您编写Expression&lt;Func&lt;dynamic, dynamic&gt;&gt; expr1 = x =&gt; 2 * x; 代码,编译器会报错,您将一事无成。但是,似乎如果您通过一种方法创建一个这样的表达式,那么编译器似乎对此很满意,并且生成的应用程序可以正常工作。这没有意义,所以我想知道窗帘后面发生了什么。

我想,在后台,ConvertExpression 返回的表达式可能是Expression&lt;Func&lt;object, object&gt;&gt; 类型,这是一个有效类型,但令我困惑的是我不能在声明中使用Expression&lt;Func&lt;dynamic, dynamic&gt;&gt; 类型并且但是我可以将它用作方法的返回类型。请参阅下面的示例。

非常感谢!

public class ExpressionExample
{
    public void Main()
    {
        // Doesn't compile:
        //Expression<Func<dynamic, dynamic>> expr1 = x => 2 * x;

        // Compiles and works - OK
        Expression<Func<double, double>> expr2 = x => 2 * x;
        Func<double, double> func2 = (Func<double, double>)expr2.Compile();
        Console.WriteLine(func2(5.0).ToString()); // Outputs 10

        // Compiles and works - ??? This is the confusing block...
        Expression<Func<dynamic, dynamic>> expr3 = ConvertExpression(expr2);
        Func<dynamic, dynamic> func3 = (Func<dynamic, dynamic>)expr3.Compile();
        Console.WriteLine(func3(5.0).ToString()); // Outputs 10

        // Side note: compiles and works:
        Expression<Func<object, object>> expr4 = x => double.Parse(2.ToString()) * double.Parse(x.ToString());
        Func<object, object> func4 = (Func<object, object>)expr4.Compile();
        Console.WriteLine(func4(5.0).ToString()); // Outputs 10
    }

    private Expression<Func<dynamic, dynamic>> ConvertExpression<TInput, TOutput>(Expression<Func<TInput, TOutput>> expression)
    {
        Expression<Func<object, TInput>> convertToInput = value => (TInput)value;
        // The following doesn't compile: var input = Expression.Parameter(typeof(dynamic), "input");

        var input = Expression.Parameter(typeof(object), "input");        

        Expression<Func<TOutput, dynamic>> convertToOutput = value => (dynamic)value;

        var body = Expression.Invoke(convertToOutput, Expression.Invoke(expression, Expression.Invoke(convertToInput, input)));
        var lambda = Expression.Lambda<Func<dynamic, dynamic>>(body, input);

        return lambda;
    }
}

【问题讨论】:

标签: c# dynamic .net-4.0 lambda expression-trees


【解决方案1】:

我想,在后台,ConvertExpression 返回的表达式可能是Expression&lt;Func&lt;object, object&gt;&gt; 类型,这是一个有效类型

正确。

我不能在声明中使用Expression&lt;Func&lt;dynamic, dynamic&gt;&gt; 类型,但我可以将它用作方法的返回类型。

这部分陈述不正确。正如您在示例中所指出的,在局部变量的声明中使用该类型是完全合法的。

不合法的位是在被转换为表达式树类型的 lambda 内执行动态操作。具体的表达式树类型无关;重要的是操作是动态的。

【讨论】:

  • @Eric 基本上(仍然?)不支持 lambdas 中的动态操作。会有吗?
  • @Maghis:StackOverflow 不适合提出需要预测未来的问题。我们现在没有此类功能的计划;我们是否会在未来二十年,我怎么知道?
  • @Eric 对不起,由于我的英语不好,您误会了。我只是想知道您是否有任何支持它的计划,“我们现在没有计划提供这样的功能”正是我想要的,谢谢
【解决方案2】:

我在尝试您的代码时遇到的编译器错误是“错误 CS1963:表达式树可能不包含动态操作”。我将问题行更改为Expression&lt;Func&lt;dynamic, dynamic&gt;&gt; expr1 = x =&gt; x;(从 lambda 中删除“操作”)并且它有效!因此,您可以在表达式中使用动态,但您实际上不能对它们执行任何“操作”。不是很有帮助,我知道。在我的测试中,即使.ToString() 也算作一次操作。

【讨论】:

  • 谢谢 - 我看到了同样的......我认为这让事情变得更加奇怪 - 所以,如果有一些操作,那么它就不会编译。但是,如果没有,则编译。这不与运行时解析操作、方法调用等的动态类型背后的哲学相矛盾吗?
  • 我们在编译时为那些在运行时实现动态语义的动态操作生成的代码生成非常复杂;足够复杂以至于没有简单的方法可以在表达式树中清晰地表示它。基本上,选择归结为“不允许它们出现在表达式树中”、“延迟发货”或“发货车”。我们选择了前者。如果有足够的需求,我们总是可以在未来的版本中将此作为功能添加;如果您可以描述激发此功能的引人入胜的用户场景,那么在预算功能工作时会有所帮助。
猜你喜欢
  • 1970-01-01
  • 2012-05-29
  • 1970-01-01
  • 1970-01-01
  • 2021-05-02
  • 2015-01-13
  • 2019-05-24
  • 2021-09-08
  • 2019-06-07
相关资源
最近更新 更多