Lambda表达式 - C#编译器推断

时间:2018-01-01 17:20:35

标签: c# expression-trees type-inference

我试图理解编译器推断行为。例如,从下面的代码片段中,已编译的委托的运行时类型为Func<int>

Expression addExpr = Expression.Add(Expression.Constant(2), Expression.Constant(1));
LambdaExpression lambdaExpr1 = Expression.Lambda(addExpr, null);
var compiled = lambdaExpr1.Compile();

但是下面的代码行不能编译。不确定编译器隐含地将其转换为Expression<Func<int>>并分配给变量以键入LambdaExpression

的含义不明确
LambdaExpression lambdaExp2 = ()=>2+1;

我可以猜到右边的lambda表达式的一个可能原因也可以匹配我的自定义委托的签名,在这种情况下,编译器无法推断委托类型。但同样的原因适用于我的初始代码段。为什么CLR(运行时)可以决定它可以是Func<int>类型。如果这是可能的并且通过运行时足够的推断那么为什么c#编译器也可以做同样的事情。

2 个答案:

答案 0 :(得分:8)

首先让我以更简洁的形式重新提出问题。

  

lambda表达式可以转换为var dicStatus = new Dictionary<int, string> { { 0, "Pending" }, { 1, "Booked" }, { 2, "Cancelled" } // ... }; string querySql = " SELECT * FROM View_Booking" + " WHERE CAST(bkID AS NVARCHAR(10)) LIKE @bkID" + " OR bkSlot LIKE @bkSlot" + " OR bkStatus = @status"; using (SqlConnection dbConn = new SqlConnection(connectionString)) { dbConn.Open(); using (SqlCommand sqlCommand = new SqlCommand(querySql, dbConn)) { sqlCommand.Parameters.Add("@bkID", SqlDbType.VarChar).value ="%" + keyword + "%"; sqlCommand.Parameters.Add("@bkSlot", SqlDbType.VarChar).value ="%" + keyword + "%"; sqlCommand.Parameters.Add("@status", SqlDbType.Int).value = dicStatus.FirstOrDefault(x => x.Value == keyword).Key; sqlCommand.ExecuteNonQuery(); } } ,其中sqlCommand.Parameters.Add("@bkID", SqlDbType.Int).value = (Int)keyword ; 是与lambda表达式兼容的委托类型。 Expression<T>源自T。为什么我们不能将lambda表达式直接转换为Expression<T>

原因是:因为这是C#中罕见的情况之一,其中类型信息从分配目标的类型流向被分配事物的类型 。转换lambda时编译器必须知道委托类型; C#编译器不会从lambda中推断出委托的类型,而是验证lambda是否与给定的委托类型兼容。

现在,我们是否可以设计并实施一个系统,如果类型信息不存在且转换目标为LambdaExpression,那么会进行合理的猜测吗?当然。除了没有人想要或需要这个功能之外,没有什么能阻止我们。

但问题的回答是“为什么这个功能不存在?”总是一样的。 我们无需说明不实施功能的理由。没有人要求提供该功能,没有人设计,实施,测试或将其发送给客户。因此,没有任何功能。

相反,不存在的功能需要倡导者开始将它们变为现有功能。如果这是您想要或需要的功能,那么请在github论坛上进行讨论并提倡该功能。或者自己实施;编译器是开源的。

答案 1 :(得分:-1)

我认为你将匿名的lambdas和代表与命名的混合;请考虑以下声明:

delegate int del();

虽然第一个代码段中的表达式与此委托的签名匹配,但由于LambdaExpression返回匿名类型,因此无法将其转换为此类型:

var mydel = compiled as del; // Returns null

另一方面,如果您从类型委托构建表达式,即使在编译之后它仍将是签名:

Expression<del> exp = () => 2 + 1;
LambdaExpression lambdaExp2 = exp;
var compiled2 = lambdaExp2.Compile();

var mydel2 = compiled2 as del; // This will work