19

我在篡改 Expressions 并且在某些时候感到困惑

  1. 我们可以将相同的 LamdaExpression 分配给 Expression 和/或 Func。但是我们不能将 Func 分配给表达式(或将表达式分配给 Func)。为什么我们不能这样做?我查找是否定义了 Expression 和 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. 我认为编译器有一些东西。如果是这样,我们是否可以编写我们的自定义类型以这种(令人困惑的)方式表现并利用某些东西。

4

3 回答 3

33

关于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;

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

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

关于您的最后一个问题:您可以在复杂类型之间创建自定义的隐式或显式转换,以便可以将这种“魔术”应用于分配。有关详细信息,请参阅转换操作编程指南

于 2013-01-05T23:05:52.453 回答
4

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

当您编写 lambda 表达式时,编译器会执行以下操作:生成与您的 lamda 表达式匹配的成员函数并将其分配给您的 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
            );
    } 
}

你看这是完全不同的事情,这就是为什么你不能简单地把一个扔给另一个。

于 2015-02-04T09:21:37.837 回答
0

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

如果它按您的预期工作,它需要做的是获取 lambda 函数的抽象语法树并生成表达式对象树。好吧,据我所知,这实际上是不可能的,那又有什么好处呢?

于 2013-01-05T19:34:02.133 回答