将 Func 分配给表达式,反之亦然
Posted
技术标签:
【中文标题】将 Func 分配给表达式,反之亦然【英文标题】:Assigning a Func to an Expression and vice versa 【发布时间】:2012-12-19 23:25:29 【问题描述】:我篡改了表达式,我在某些时候感到困惑
我们可以将相同的 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>'
即使我们不能将 LambdaExpression 分配给对象。再说一遍,为什么我们不能这样做?
// object o = i => i + i; // Cannot convert source type 'lambda expression' to target type 'object'
我认为编译器有些问题。如果是这样,我们是否可以编写我们的自定义类型以这种(令人困惑的)方式表现并利用某些东西。
【问题讨论】:
您可以在表达式树上调用 compile() 来获取函数。另一种方式在很大程度上是不可能的:它需要一个 AST,对于任意 IL,这很难构建 【参考方案1】:关于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 了解更多信息。
【讨论】:
【参考方案2】:实际上这两个表达式都是语法糖,由编译器转换成不同的语言结构。
当您编写 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
);
你看这是完全不同的东西,这就是为什么你不能简单地将一个转换为另一个。
【讨论】:
【参考方案3】:表达式是一种抽象。它允许 Linq 有效地为数据库、XML 或其他数据源构建 SQL 查询。它在概念上类似于抽象语法树,它存储查询的语法元素,以便 Linq 提供者可以构建查询。
如果它按您的预期工作,它需要做的是获取 lambda 函数的抽象语法树并生成表达式对象树。嗯,据我所知这不太可能,那有什么好处呢?
【讨论】:
为什么不可能?当然,您可以遍历抽象语法树并生成对象的递归集合。这是否有用是另一个问题。 当然可以。可能有更好的方法,但最坏的情况是您可以对 IL 进行逆向工程(以编程方式)并生成表达式树。但为什么呢? 我很确定如果你让它们成为一个真正的方法(而不是 lambda),你可以使用表达式树中的单个“invoke”节点来运行该函数。如果您需要任意 lambda,我不确定该怎么做。以上是关于将 Func 分配给表达式,反之亦然的主要内容,如果未能解决你的问题,请参考以下文章