将函数赋值给表达式,反之亦然

本文关键字:反之亦然 表达式 函数 赋值 | 更新日期: 2023-09-27 17:50:44

我在篡改表达式,我在某些地方感到困惑

  1. 可以将同一个LamdaExpression赋值给Expression和/或Func。但不能将Func赋值给表达式(或将表达式赋值给Func)。为什么我们不能这样做呢?我查找了表达式和Func之间的转换操作符是否定义,但我找不到任何。

    Func<int, int> sumFunc            = i => i + i;
    Expression<Func<int, int>> sumExp = i => i + i;
    // sumExp = sumFunc; // Cannot convert source type 'System.Func<int,int>' to target type 'System.Linq.Expressions.Expression<System.Func<int,int>>'
    // sumFunc = sumExp; // Cannot convert source type 'System.Linq.Expressions.Expression<System.Func<int,int>>' to target type 'System.Func<int,int>'
    
  2. 甚至我们也不能将LambdaExpression赋值给对象。为什么我们不能这样做呢?

    // object o = i => i + i; // Cannot convert source type 'lambda expression' to target type 'object'
    
  3. 我认为有一些关于编译器。如果是这样,我们是否可以编写这样(令人困惑的)行为的自定义类型并利用某些东西呢?

将函数赋值给表达式,反之亦然

关于c#语言规范的lambda表达式,如

i => i + i

是一个匿名函数。具有此分类的表达式可以隐式地转换为兼容的委托类型或表达式树类型。这就是为什么你可以同时写

Func<int, int> sumFunc            = i => i + i;
Expression<Func<int, int>> sumExp = i => i + i;

第一个是委托类型,第二个是表达式树类型。因为在这些类型之间没有隐式转换,所以不能赋值sumFunc = sumExp,反之亦然。但是由于表达式树sumExp表示lambda表达式,您可以将该表达式编译为可执行委托并将其分配给sumFunc,因为这是这样一个兼容的委托:

sumFunc = sumExp.Compile();

另一个方向是不可能的,因为委托不容易被"反编译"成表达式树。

不能写

的原因
object o = i => i + i;

是,匿名函数本身没有值或类型,它只是可以转换为委托或表达式树类型。您必须告诉编译器您想要哪一个,因此您可以首先转换它,然后将结果分配给object类型的变量:

object sumFuncObject = (Func<int, int>) (i => i + i);
object sumExpObject = (Expression<Func<int, int>>) (i => i + i);

关于你的最后一个问题:你可以在复杂类型之间创建自定义的隐式或显式转换,这样这种"魔法"就可以应用于赋值。有关更多信息,请参阅转换操作编程指南。

实际上这两个表达式都是语法糖,被编译器转换成不同的语言结构。

当你编写lambda表达式时,编译器会这样做:生成与lambda表达式匹配的成员函数,并将其分配给你的sumFunc变量(这不是确切的代码,只是为了获得一个概念):

class Program
{
   private static int generatedname(int i)
   {
        return i + i;
   }
   static void Main()
   {
       Func<int, int> sumFunc = generatedname;
   } 
}

当你写表达式树时,会有更多神奇的事情发生。在编译时,编译器将表达式转换为表达式树的"构造"。这样的。

class Program
{
    static void Main()
    {
        var addPrm = Expression.Parameter(typeof(int), "i");
        Expression<Func<int, int>> sumExp = 
            Expression.Lambda<Func<int, int>>(
                Expression.Add(
                    addPrm,
                    addPrm
                ),
                addPrm
            );
    } 
}

你看这是完全不同的东西,这就是为什么你不能简单地将一个转换成另一个

表达式是一种抽象。它允许Linq有效地为数据库、XML或其他数据源构建SQL查询。它在概念上类似于抽象语法树,它存储查询的语法元素,以便Linq提供程序可以构建查询。

如果它如您所期望的那样工作,它必须做的是获取lambda函数的抽象语法树并生成表达式对象树。据我所知,那是不可能的,而且那又有什么好处呢?