简体   繁体   English

Roslyn无法编译代码

[英]Roslyn failed to compile code

After I have migrated my project from VS2013 to VS2015 the project no longer builds. 将项目从VS2013迁移到VS2015之后,项目不再构建。 A compilation error occurs in the following LINQ statement: 以下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();
}

The compiler returns an error: 编译器返回错误:

Error CS0165 Use of unassigned local variable 'b' 错误CS0165使用未分配的局部变量'b'

What causes this issue? 是什么导致这个问题? Is it possible to fix it through a compiler setting? 是否可以通过编译器设置来修复它?

What does cause this issue? 是什么导致了这个问题?

Looks like a compiler bug to me. 看起来像编译器错误给我。 At least, it did. 至少,确实如此。 Although the decimal.TryParse(v, out a) and decimal.TryParse(v, out b) expressions are evaluated dynamically, I expected the compiler to still understand that by the time it reaches a <= b , both a and b are definitely assigned. 虽然decimal.TryParse(v, out a)decimal.TryParse(v, out b)表达式是动态计算的,但我希望编译器仍然能够理解,当它到达a <= bab都是绝对的分配。 Even with the weirdnesses you can come up with in dynamic typing, I'd expect to only ever evaluate a <= b after evaluating both of the TryParse calls. 即使你可以在动态类型中提出奇怪的想法,我也希望在评估两个TryParse调用之后只评估a <= b

However, it turns out that through operator and conversion tricky, it's entirely feasible to have an expression A && B && C which evaluates A and C but not B - if you're cunning enough. 然而,事实证明,通过操作符和转换技巧,如果你足够狡猾,有一个表达式A && B && C来评估AC而不是B - 这是完全可行的。 See the Roslyn bug report for Neal Gafter's ingenious example. 有关Neal Gafter巧妙的例子,请参阅Roslyn错误报告

Making that work with dynamic is even harder - the semantics involved when the operands are dynamic are harder to describe, because in order to perform overload resolution, you need to evaluate operands to find out what types are involved, which can be counter-intuitive. 使用dynamic工作更加困难 - 当操作数是动态的时所涉及的语义更难描述,因为为了执行重载解析,您需要评估操作数以找出涉及的类型,这可能是违反直觉的。 However, again Neal has come up with an example which shows that the compiler error is required... this isn't a bug, it's a bug fix . 然而,Neal再次提出了一个示例,该示例显示编译器错误是必需的...这不是错误,这是一个错误修复 Huge amounts of kudos to Neal for proving it. 为了证明这一点,Neal获得了大量的赞誉。

Is it possible to fix it through compiler settings? 是否可以通过编译器设置来修复它?

No, but there are alternatives which avoid the error. 不,但有其他方法可以避免错误。

Firstly, you could stop it from being dynamic - if you know that you'll only ever use strings, then you could use IEnumerable<string> or give the range variable v a type of string (ie from string v in array ). 首先,你可以阻止它变得动态 - 如果你知道你只会使用字符串,那么你可以使用IEnumerable<string> 或者给范围变量v一个string类型(即from string v in array )。 That would be my preferred option. 那将是我的首选。

If you really need to keep it dynamic, just give b a value to start with: 如果你真的需要保持动态,只需给b一个值开头:

decimal a, b = 0m;

This won't do any harm - we know that actually your dynamic evaluation won't do anything crazy, so you'll still end up assigning a value to b before you use it, making the initial value irrelevant. 这不会造成任何伤害 - 我们知道实际上你的动态评估不会做任何疯狂的事情,所以你仍然会在使用它之前为b赋值,使初始值无关紧要。

Additionally, it seems that adding parentheses works too: 此外,似乎添加括号也有效:

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

That changes the point at which various pieces of overload resolution are triggered, and happens to make the compiler happy. 这改变了触发各种重载决策的点,并使编译器感到高兴。

There is one issue still remaining - the spec's rules on definite assignment with the && operator need to be clarified to state that they only apply when the && operator is being used in its "regular" implementation with two bool operands. 还有一个问题仍然存在 - 规范关于使用&&运算符进行明确赋值的规则需要明确说明它们仅在&&运算符用于其具有两个bool操作数的“常规”实现中时才适用。 I'll try to make sure this is fixed for the next ECMA standard. 我将尝试确保这是针对下一个ECMA标准修复的。

This does appear to be a bug, or at the least a regression, in the Roslyn compiler. 这似乎是Roslyn编译器中的错误,或者至少是回归。 The following bug has been filed to track it: 已提交以下错误以跟踪它:

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

In the meantime, Jon's excellent answer has a couple of work arounds. 与此同时,Jon的优秀答案还有几个方面。

Since I got schooled so hard in the bug report, I'm going to try to explain this myself. 由于我在错误报告中受过如此刻苦的教育,我将尝试自己解释一下。


Imagine T is some user-defined type with an implicit cast to bool that alternates between false and true , starting with false . 想象T是一些用户定义的类型与隐式转换为bool其之间交替falsetrue ,从false As far as the compiler knows, the dynamic first argument to the first && might evaluate to that type, so it has to be pessimistic. 就编译器所知,第一个&&dynamic第一个参数可能会计算为该类型,因此它必须是悲观的。

If, then, it let the code compile, this could happen: 如果,那么,它让代码编译,这可能发生:

  • When the dynamic binder evaluates the first && , it does the following: 当动态绑定器评估第一个&& ,它执行以下操作:
    • Evaluate the first argument 评估第一个参数
    • It's a T - implicitly cast it to bool . 这是一个T - 隐含地将它投射到bool
    • Oh, it's false , so we don't need to evaluate the second argument. 哦,这是false ,所以我们不需要评估第二个参数。
    • Make the result of the && evaluate as the first argument. && evaluate的结果作为第一个参数。 (No, not false , for some reason.) (不,不是false ,出于某种原因。)
  • When the dynamic binder evaluates the second && , it does the following: 当动态绑定器评估第二个&& ,它执行以下操作:
    • Evaluate the first argument. 评估第一个参数。
    • It's a T - implicitly cast it to bool . 这是一个T - 隐含地将它投射到bool
    • Oh, it's true , so evaluate the second argument. 哦,这是true ,所以评估第二个论点。
    • ... Oh crap, b isn't assigned. ...哦废话, b没有分配。

In spec terms, in short, there are special "definite assignment" rules that let us say not only whether a variable is "definitely assigned" or "not definitely assigned", but also if it is "definitely assigned after false statement" or "definitely assigned after true statement". 简而言之,有一些特殊的“明确赋值”规则,它们不仅可以说明变量是“明确赋值”还是“未明确赋值”,而且还可以说“在false陈述之后明确赋值”或“在true陈述后明确指定“。

These exist so that when dealing with && and || 这些存在使得在处理&&|| (and ! and ?? and ?: ) the compiler can examine whether variables may be assigned in particular branches of a complex boolean expression. (和! and ???: :)编译器可以检查是否可以在复杂布尔表达式的特定分支中分配变量。

However, these only work while the expressions' types remain boolean . 但是,这些只在表达式的类型保持布尔值时才起作用。 When part of the expression is dynamic (or a non-boolean static type) we can no longer reliably say that the expression is true or false - the next time we cast it to bool to decide which branch to take, it may have changed its mind. 当表达式的一部分是dynamic (或非布尔静态类型)时,我们再也不能可靠地说表达式为truefalse - 下次我们将它转​​换为bool来决定采用哪个分支时,它可能已经改变了它心神。


Update: this has now been resolved and documented : 更新:现在已经解决记录在案

The definite assignment rules implemented by previous compilers for dynamic expressions allowed some cases of code that could result in variables being read that are not definitely assigned. 先前编译器为动态表达式实现的明确赋值规则允许某些代码可能导致读取的变量未明确赋值。 See https://github.com/dotnet/roslyn/issues/4509 for one report of this. 有关此问题的报告,请参阅https://github.com/dotnet/roslyn/issues/4509

... ...

Because of this possibility the compiler must not allow this program to be compiled if val has no initial value. 由于这种可能性,如果val没有初始值,编译器不得允许编译该程序。 Previous versions of the compiler (prior to VS2015) allowed this program to compile even if val has no initial value. 先前版本的编译器(在VS2015之前)允许该程序编译,即使val没有初始值。 Roslyn now diagnoses this attempt to read a possibly uninitialized variable. 罗斯林现在诊断这种尝试读取可能未初始化的变量。

This is not a bug. 这不是一个错误。 See https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713 for an example of how a dynamic expression of this form can leave such an out variable unassigned. 有关此表单的动态表达式如何使这样的out变量未分配的示例,请参阅https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM