将项目从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();
}
编译器返回错误:
是什么原因导致此问题?是否可以通过编译器设置修复它?
最佳答案
对我来说似乎是一个编译器错误。至少,确实如此。尽管decimal.TryParse(v, out a)
和decimal.TryParse(v, out b)
表达式是动态求值的,但我希望编译器仍能理解,到a <= b
时,a
和b
都已明确分配。即使您在动态类型输入中可能会遇到一些奇怪问题,我也希望仅在评估两个a <= b
调用之后才评估TryParse
。
但是,事实证明,通过运算符和转换比较棘手,如果表达式足够复杂,那么使用A && B && C
表达式评估A
和C
而不是B
完全可行。有关Neal Gafter的巧妙示例,请参见Roslyn bug report。
使用dynamic
更加困难-操作数动态时涉及的语义更难描述,因为为了执行重载解析,您需要评估操作数以找出涉及的类型,这可能是违反直觉的。但是,Neal再次提出了一个示例,该示例表明需要编译器错误……这不是错误,而是错误修复。尼尔(Neal)证明了这一点非常感谢。
否,但是有其他选择可以避免该错误。
首先,您可以阻止它动态化-如果您知道只使用字符串,则可以使用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)
这改变了触发各种重载解析的时间点,并且碰巧使编译器满意。
仍然存在一个问题-需要明确规范
&&
运算符的明确赋值规则,以声明它们仅在&&
运算符用于其带有两个bool
操作数的“常规”实现中时才适用。我将尝试确保为下一个ECMA标准修复此问题。