在我将我的项目从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"

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

推荐答案

是什么导致了这个问题?

在我看来像是编译器错误.至少,它做到了.虽然decimal.TryParse(v, out a)decimal.TryParse(v, out b)表达式是动态求值的,但我expected要求编译器仍然理解,当它达到a <= b时,ab都被明确赋值.即使您可以在动态类型中想出一些奇怪的东西,在判断了这两个TryParse个调用之后,我预计也只会判断a <= b.

然而,事实证明,通过运算符和转换技巧,完全可以得到一个计算AC但不是B的表达式A && B && C——如果你足够狡猾的话.Neal Gafter的巧妙例子见Roslyn bug report.

使用dynamic更难——当操作数是动态的时,所涉及的语义更难描述,因为为了执行重载解析,您需要计算操作数以找出所涉及的类型,这可能是违反直觉的.然而,Neal再次给出了一个例子,表明编译器错误是必需的...这不是bug,这是bug fix.尼尔证明了这一点,这是一个巨大的荣誉.

可以通过编译器设置来修复它吗?

没有,但也有避免错误的替代方案.

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

如果really需要保持动态,只需在开始时给b一个值:

decimal a, b = 0m;

这不会造成任何伤害——我们知道actually动态判断不会做任何疯狂的事情,因此在使用它之前,你仍然会将一个值赋给b,从而使初始值变得无关紧要.

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

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

这改变了各种重载解析被触发的点,并且碰巧让编译器感到高兴.

还有one个问题需要解决——规范中关于&&运算符的确定赋值规则需要澄清,以说明它们仅适用于&&运算符在其具有两个bool操作数的"常规"实现中使用的情况.我会尽量确保这是固定的下一个ECMA标准.

.net相关问答推荐

无法使用 int.Parse 从字符串转换值

如何使用 Moq 为不同的参数设置两次方法

如何创建 LINQ to SQL 事务?

每 X 秒执行一次指定函数

Environment.TickCount 与 DateTime.Now

IIS Express - 500.19 无法读取配置文件 - 因为它正在查看错误的路径

C# 中基于接口编程的运算符重载

为什么 Interlocked.Exchange 不支持布尔类型?

什么是 project.lock.json?

如何使用反射在 .NET 中调用重载方法

基于多个字符分隔符拆分字符串

如何比较 C# 中的(目录)路径?

X509Certificate 构造函数异常

微软内部 PriorityQueue 中的错误?

您可以使用 Xamarin 开发 Linux 应用程序吗?

静态方法继承的正确替代方法是什么?

带有嵌套控件的设计模式

WinForms 中的模型视图演示者

C# 应用程序中的资源和嵌入式资源有什么区别?

如何为 Dapper 查询动态创建参数