首页 文章

Roslyn无法编译代码

提问于
浏览
94

将项目从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 回答

  • 21

    导致这个问题的原因是什么?

    看起来像编译器错误给我 . 至少,确实如此 . 尽管 decimal.TryParse(v, out a)decimal.TryParse(v, out b) 表达式是动态计算的,但我希望编译器仍然能够理解,当它到达 a <= b 时, ab 都是明确分配的 . 即使你可以在动态类型中提出奇怪的想法,我也希望在评估两个 TryParse 调用后才能评估 a <= b .

    然而,事实证明,通过操作符和转换技巧,如果你足够狡猾的话,有一个表达式 A && B && C 来评估 AC 而不是 B 是完全可行的 . 请参阅Roslyn bug report了解Neal Gafter的巧妙例子 .

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

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

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

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

    如果你真的需要保持动态,只需给_1151813_一个值开头:

    decimal a, b = 0m;
    

    这不会造成任何伤害 - 我们知道实际上你的动态评估仍然会在你使用它之前最终为 b 赋值,使得初始值无关紧要 .

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

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

    这改变了触发各种重载决策的点,并使编译器感到高兴 .

    还有一个问题仍然存在 - 需要澄清规范关于使用 && 运算符进行明确赋值的规则,以声明它们仅在 && 运算符在其"regular"实现中使用两个 bool 操作数时才适用 . 我将尝试确保这是针对下一个ECMA标准修复的 .

  • 16

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

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

    在此期间,Jon的excellent answer有几个工作 .

  • 111

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


    想象一下 T 是一些用户定义的类型,隐式转换为 bool ,在 falsetrue 之间交替,从 false 开始 . 就编译器所知,第一个 && 的第一个参数可能会评估为该类型,因此它必须是悲观的 .

    如果,那么,它让代码编译,这可能发生:

    • 当动态 Binders 评估第一个 && 时,它执行以下操作:

    • 评估第一个参数

    • 这是一个 T - 隐式地将其强制转换为 bool .

    • 哦,这是 false ,所以我们不需要评估第二个参数 .

    • && evaluate的结果作为第一个参数 . (不,不是 false ,出于某种原因 . )

    • 当动态 Binders 评估第二个 && 时,它执行以下操作:

    • 评估第一个参数 .

    • 这是一个 T - 隐含地将其强制转换为 bool .

    • 哦,这是 true ,所以评估第二个参数 .

    • ...哦废话, b 未分配 .


    在规范方面,简而言之,有一些特殊的规则让我们不仅可以说变量是"definitely assigned"还是"not definitely assigned",而且还可以说“在 true 语句之后 false 语句" or "之后明确赋值” .

    这些存在,以便在处理 &&|| (和 ! )时和 ???: )编译器可以检查是否可以在复杂布尔表达式的特定分支中分配变量 .

    但是,这些只在表达式的类型保持布尔值时才起作用 . 当表达式的一部分是 dynamic (或非布尔静态类型)时,我们再也无法可靠地说表达式是 truefalse - 下次我们将它转换为 bool 以决定采取哪个分支时,它可能已经改变了它心神 .


    更新:现在已经resolveddocumented

    先前编译器为动态表达式实现的明确赋值规则允许某些代码可能导致读取的变量未明确赋值 . 有关此问题的报告,请参阅https://github.com/dotnet/roslyn/issues/4509 . ...由于这种可能性,如果val没有初始值,编译器不得允许编译该程序 . 先前版本的编译器(在VS2015之前)允许该程序编译,即使val没有初始值 . 罗斯林现在诊断这种尝试读取可能未初始化的变量 .

  • 15

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

相关问题