Roslyn 编译代码失败
Roslyn failed to compile code
在我将我的项目从 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();
}
编译器returns出错:
Error CS0165 Use of unassigned local variable 'b'
导致此问题的原因是什么?是否可以通过编译器设置修复它?
What does cause this issue?
在我看来像是一个编译器错误。至少,它做到了。虽然 decimal.TryParse(v, out a)
和 decimal.TryParse(v, out b)
表达式是动态求值的,但我 期望 编译器仍然理解当它到达 a <= b
时,两者 a
和 b
是绝对赋值的。即使您在动态类型中可能会遇到一些奇怪的事情,我也希望在评估两个 TryParse
调用之后只评估 a <= b
。
然而,事实证明,通过运算符和转换技巧,有一个计算 A
和 C
而不是 B
的表达式 A && B && C
是完全可行的 -如果你足够狡猾。请参阅 Roslyn bug report 了解 Neal Gafter 的巧妙示例。
使用 dynamic
更难 - 当操作数是动态时涉及的语义更难描述,因为为了执行重载解析,您需要评估操作数以找出哪些类型是动态的涉及,这可能是违反直觉的。但是,Neal 再次提出了一个示例,表明编译器错误是必需的...这不是错误,而是错误 fix。 Neal 证明了这一点,获得了巨大的荣誉。
Is it possible to fix it through compiler settings?
否,但有其他方法可以避免该错误。
首先,您可以阻止它成为动态的 - 如果您知道您只会使用字符串,那么您可以使用 IEnumerable<string>
或 给出范围变量 v
是 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)
这改变了触发各种重载决议的时间点,恰好让编译器满意。
仍然存在 一个 问题 - 规范中关于使用 &&
运算符的明确赋值的规则需要澄清,以说明它们仅适用于 &&
运算符在其 "regular" 实现中使用,带有两个 bool
操作数。我会努力确保在下一个 ECMA 标准中解决这个问题。
这确实是 Roslyn 编译器中的错误,或者至少是回归。已提交以下错误以对其进行跟踪:
与此同时,Jon 的 有一些变通办法。
由于我在错误报告中受到了如此严重的教育,我将尝试自己解释这一点。
假设 T
是一些用户定义的类型,隐式转换为 bool
,在 false
和 true
之间交替,从 false
开始。据编译器所知,第一个 &&
的 dynamic
第一个参数可能会评估为该类型,因此它必须是悲观的。
如果让代码编译,可能会发生这种情况:
- 当动态联编程序计算第一个
&&
时,它会执行以下操作:
- 计算第一个参数
- 这是一个
T
- 隐式地将其转换为 bool
。
- 哦,是
false
,所以我们不需要计算第二个参数。
- 将
&&
的结果作为第一个参数求值。 (不,不是 false
,出于某种原因。)
- 当动态联编程序计算第二个
&&
时,它会执行以下操作:
- 计算第一个参数。
- 这是一个
T
- 隐式地将其转换为 bool
。
- 哦,是
true
,所以计算第二个参数。
- ...糟糕,
b
未分配。
在规范方面,简而言之,有特殊的“明确赋值”规则,让我们不仅可以说一个变量是“明确赋值”还是“非明确赋值”,还可以判断它是否“在之后明确赋值” false
语句”或“在 true
语句之后明确赋值”。
这些存在是为了在处理 &&
和 ||
(以及 !
和 ??
和 ?:
)时,编译器可以检查变量是否可能在复杂布尔表达式的特定分支中分配。
但是,这些仅在 表达式的类型保持为布尔值时有效。当表达式的一部分是 dynamic
(或非布尔静态类型)时,我们不能再可靠地说表达式是 true
或 false
- 下次我们将其转换为 bool
决定走哪个分支,可能已经改变主意了。
更新:现在已经 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.
...
Because of this possibility the compiler must not allow this program to be compiled if val has no initial value. Previous versions of the compiler (prior to VS2015) allowed this program to compile even if val has no initial value. Roslyn now diagnoses this attempt to read a possibly uninitialized variable.
这不是错误。有关此形式的动态表达式如何不分配此类 out 变量的示例,请参阅 https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713。
在我将我的项目从 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();
}
编译器returns出错:
Error CS0165 Use of unassigned local variable 'b'
导致此问题的原因是什么?是否可以通过编译器设置修复它?
What does cause this issue?
在我看来像是一个编译器错误。至少,它做到了。虽然 decimal.TryParse(v, out a)
和 decimal.TryParse(v, out b)
表达式是动态求值的,但我 期望 编译器仍然理解当它到达 a <= b
时,两者 a
和 b
是绝对赋值的。即使您在动态类型中可能会遇到一些奇怪的事情,我也希望在评估两个 TryParse
调用之后只评估 a <= b
。
然而,事实证明,通过运算符和转换技巧,有一个计算 A
和 C
而不是 B
的表达式 A && B && C
是完全可行的 -如果你足够狡猾。请参阅 Roslyn bug report 了解 Neal Gafter 的巧妙示例。
使用 dynamic
更难 - 当操作数是动态时涉及的语义更难描述,因为为了执行重载解析,您需要评估操作数以找出哪些类型是动态的涉及,这可能是违反直觉的。但是,Neal 再次提出了一个示例,表明编译器错误是必需的...这不是错误,而是错误 fix。 Neal 证明了这一点,获得了巨大的荣誉。
Is it possible to fix it through compiler settings?
否,但有其他方法可以避免该错误。
首先,您可以阻止它成为动态的 - 如果您知道您只会使用字符串,那么您可以使用 IEnumerable<string>
或 给出范围变量 v
是 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)
这改变了触发各种重载决议的时间点,恰好让编译器满意。
仍然存在 一个 问题 - 规范中关于使用 &&
运算符的明确赋值的规则需要澄清,以说明它们仅适用于 &&
运算符在其 "regular" 实现中使用,带有两个 bool
操作数。我会努力确保在下一个 ECMA 标准中解决这个问题。
这确实是 Roslyn 编译器中的错误,或者至少是回归。已提交以下错误以对其进行跟踪:
与此同时,Jon 的
由于我在错误报告中受到了如此严重的教育,我将尝试自己解释这一点。
假设 T
是一些用户定义的类型,隐式转换为 bool
,在 false
和 true
之间交替,从 false
开始。据编译器所知,第一个 &&
的 dynamic
第一个参数可能会评估为该类型,因此它必须是悲观的。
如果让代码编译,可能会发生这种情况:
- 当动态联编程序计算第一个
&&
时,它会执行以下操作:- 计算第一个参数
- 这是一个
T
- 隐式地将其转换为bool
。 - 哦,是
false
,所以我们不需要计算第二个参数。 - 将
&&
的结果作为第一个参数求值。 (不,不是false
,出于某种原因。)
- 当动态联编程序计算第二个
&&
时,它会执行以下操作:- 计算第一个参数。
- 这是一个
T
- 隐式地将其转换为bool
。 - 哦,是
true
,所以计算第二个参数。 - ...糟糕,
b
未分配。
在规范方面,简而言之,有特殊的“明确赋值”规则,让我们不仅可以说一个变量是“明确赋值”还是“非明确赋值”,还可以判断它是否“在之后明确赋值” false
语句”或“在 true
语句之后明确赋值”。
这些存在是为了在处理 &&
和 ||
(以及 !
和 ??
和 ?:
)时,编译器可以检查变量是否可能在复杂布尔表达式的特定分支中分配。
但是,这些仅在 表达式的类型保持为布尔值时有效。当表达式的一部分是 dynamic
(或非布尔静态类型)时,我们不能再可靠地说表达式是 true
或 false
- 下次我们将其转换为 bool
决定走哪个分支,可能已经改变主意了。
更新:现在已经 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.
...
Because of this possibility the compiler must not allow this program to be compiled if val has no initial value. Previous versions of the compiler (prior to VS2015) allowed this program to compile even if val has no initial value. Roslyn now diagnoses this attempt to read a possibly uninitialized variable.
这不是错误。有关此形式的动态表达式如何不分配此类 out 变量的示例,请参阅 https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713。