在我将我的项目从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相关问答推荐

Docker失败文件找不到

.NET Blazor-使用子组件中的处理程序方法进行双向数据绑定

.NET 7.0中的UseHttpsRedirection和IIS生产部署

单线程单元 - 无法实例化 ActiveX 控件

防止对话框在按钮的单击事件处理程序中关闭

Microsoft.Practices.ServiceLocation 来自哪里?

DateTime.TryParseExact() 拒绝有效格式

如何结合 ||条件语句中的运算符

寻找 .NET 的命令行参数解析器

在 .NET 中填充整数列表

如何使用配置转换删除 ConnectionString

react 式扩展使用的好例子

如何将 WebResponse.GetResponseStream 返回转换为字符串?

C# - 你如何停止计时器?

等待 Async Void 方法调用以进行单元测试

从 C# 中的字符串中删除最后一个字符.优雅的方式?

将 Dictionary 转换为匿名对象?

对构造函数进行单元测试重要吗?

序列化和反序列化 .NET 对象的最快方法

如何使用 AutoMapper .ForMember?