Roslyn无法编译代码

时间:2015-08-12 09:37:20

标签: c# .net linq roslyn

将项目从VS2013迁移到VS2015之后,项目不再构建。以下LINQ语句中出现编译错误:

static void Main(string[] args)
{
    decimal a, b;
    IEnumerable<dynamic> array = new string[] { "10", "20", "30" };
    var result = (from v in array
                  where decimal.TryParse(v, out a) && decimal.TryParse("15", out b) && a <= b // Error here
                  orderby decimal.Parse(v)
                  select v).ToArray();
}

编译器返回错误:

  

错误CS0165使用未分配的局部变量'b'

导致此问题的原因是什么?是否可以通过编译器设置来修复它?

4 个答案:

答案 0 :(得分:111)

  

导致此问题的原因是什么?

对我来说看起来像编译器错误。至少,确实如此。虽然decimal.TryParse(v, out a)decimal.TryParse(v, out b)表达式是动态评估的,但我期望编译器在a <= b达到a时仍然能够理解ba <= b明确分配。即使你可以在动态类型中提出奇怪的想法,我也希望在评估两个TryParse调用后才能评估A && B && C

然而,事实证明,通过操作符和转换技巧,使用表达式A来评估CB而不是dynamic是完全可行的 - 如果你真狡猾。有关Neal Gafter的巧妙示例,请参阅https://jsfiddle.net/ozqneuha/

使用IEnumerable<string>工作更加困难 - 当操作数是动态的时所涉及的语义更难描述,因为为了执行重载解析,你需要评估操作数以找出涉及的类型,这可能是违反直觉的。然而,Neal再次提出了一个示例,该示例显示编译器错误是必需的...这不是错误,它是一个错误修复。为了证明这一点,Neal获得了大量的赞誉。

  

是否可以通过编译器设置修复它?

不,但有其他方法可以避免错误。

首先,你可以阻止它变得动态 - 如果你知道你只会使用字符串,那么你可以使用v 给出范围变量{{1}一种string(即from string v in array)。那将是我的首选。

如果确实需要保持动态,只需给b一个值开始:

decimal a, b = 0m;

这不会造成任何伤害 - 我们知道实际上您的动态评估不会做任何疯狂的事情,所以您仍然会在您之前为b分配值使用它,使初始值无关紧要。

此外,似乎添加括号也有效:

where decimal.TryParse(v, out a) && (decimal.TryParse("15", out b) && a <= b)

这改变了触发各种重载决策的点,并使编译器满意。

仍然存在一个问题 - 需要澄清规范与&&运算符明确赋值的规则,以声明它们仅适用于&&运算符时在其“常规”实现中使用两个bool操作数。我将尽力确保下一个ECMA标准的修复。

答案 1 :(得分:21)

这似乎是Roslyn编译器中的错误,或者至少是回归。已提交以下错误来跟踪它:

  

https://github.com/dotnet/roslyn/issues/4509

与此同时,Jon的excellent answer还有几个工作要做。

答案 2 :(得分:16)

由于我在错误报告中受过如此刻苦的教育,我将尝试自己解释一下。

想象一下,T是一种用户定义的类型,隐含强制转换为bool,在falsetrue之间交替,从false开始。就编译器所知,第一个dynamic的{​​{1}}第一个参数可能会计算为该类型,因此它必须是悲观的。

如果,它让代码编译,可能会发生这种情况:

  • 当动态活页夹评估第一个&&时,它会执行以下操作:
    • 评估第一个参数
    • 这是&& - 隐式将其投放到T
    • 哦,它是bool,所以我们不需要评估第二个参数。
    • false evaluate的结果作为第一个参数。 (不,不是&&,出于某种原因。)
  • 当动态活页夹评估第二个false时,它会执行以下操作:
    • 评估第一个参数。
    • 这是&& - 隐式将其投放到T
    • 哦,它是bool,所以要评估第二个参数。
    • ...哦,废话,true未分配。

在规范方面,简而言之,有一些特殊的“明确赋值”规则,它们不仅可以说明变量是“明确赋值”还是“未明确赋值”,而且还可以说明“{{ 1}}语句“或”在b语句后明确赋值“。

这些存在,以便在处理falsetrue(以及&&||!)时,编译器可以检查是否可以分配变量特别是复杂布尔表达式的分支。

但是,这些只能在中工作,而表达式的类型仍然是布尔值。当表达式的一部分是??(或非布尔静态类型)时,我们再也无法可靠地说表达式为?:dynamic - 下次我们将其转换为{ {1}}决定采取哪个分支,它可能已经改变了主意。

更新:现在已经resolveddocumented

  

以前的编译器为动态表达式实现的明确赋值规则允许某些代码可能导致读取的变量未明确赋值。有关此问题的报告,请参阅https://github.com/dotnet/roslyn/issues/4509

     

...

     

由于这种可能性,如果val没有初始值,编译器不得允许编译该程序。先前版本的编译器(在VS2015之前)允许该程序编译,即使val没有初始值。罗斯林现在诊断出这种尝试读取可能未初始化的变量。

答案 3 :(得分:15)

这不是错误。有关此表单的动态表达式如何使这样的out变量未分配的示例,请参阅https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713