将Func分配给表达式,反之亦然

时间:2013-01-05 19:29:17

标签: c# linq lambda expression func

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

  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. 我认为编译器有一些东西。如果是这样,我们可以编写那些以这种(令人困惑的)方式行事的自定义类型并利用某些东西。

3 个答案:

答案 0 :(得分:29)

关于C# Language Specification 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);

关于您的上一个问题:您可以在复杂类型之间创建自定义隐式或显式转换,以便可以将此“魔力”应用于分配。有关详细信息,请参阅Conversion Operations Programming Guide

答案 1 :(得分: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
            );
    } 
}

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

答案 2 :(得分:0)

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

如果它按预期工作,那么它必须采用lambda函数的抽象语法树并生成表达式对象树。嗯,就我所知,这实际上是不可能的,无论如何它会有什么好处?