【发布时间】:2016-06-04 18:00:07
【问题描述】:
在 couple showstoppers 延迟迁移到 .NET 4.6 运行时后,我终于对迁移到 C#6/VB14 编译器感到满意,直到我遇到了一个关键问题,即 VB.NET 中的迭代器函数丢弃了本地变量。
在 Visual Studio 2015 / msbuild 中以发布模式(优化)编译时,以下代码示例将在注释行上引发空引用异常。
Module Module1
Sub Main()
For Each o As Integer In GetAllStuff()
Console.WriteLine(o.ToString())
Next
Console.ReadKey()
End Sub
Private Iterator Function GetAllStuff() As IEnumerable(Of Integer)
Dim map As Dictionary(Of String, String) = New Dictionary(Of String, String)
Dim tasks As New List(Of Integer)
tasks.Add(1)
For Each task As Integer In tasks
Yield task
Next
'The value of map becomes null here under the new VB14 compiler in Release on .NET 4.6'
For Each s As String In map.Values
Yield 100
Next
End Function
End Module
所以,这很可怕。
值得注意的是,此代码的 C# 等效项可以毫无问题地执行。更重要的是,这在以前版本的 VB 编译器下有效(并且已经有效)。比较两个不同编译器创建的状态机之间的 MSIL,新编译器似乎几乎只使用 .locals 来存储局部变量,而旧编译器使用状态机上的可变字段来保存局部值。
我错过了什么吗?我无法在 VB 中找到任何关于迭代器的重大更改的文档(我也无法想象会是这种情况),但也没有发现其他任何人遇到过这个问题。
可以通过将map 的构造移到第一个foreach 循环之后来解决这个特定的示例,但是我担心的是我对这个问题的真正含义没有任何了解。我对修改代码以“让它工作”不感兴趣。在我们广泛的代码库中,我还能在哪里遇到同样的问题?我已经在Connect 上提交了这个问题,但这通常感觉就像一个黑洞。
更新
有人刚刚在 Roslyn GitHub 页面上报告了与异步状态机相同的问题:https://github.com/dotnet/roslyn/issues/9001
希望这会引起一些关注。
【问题讨论】:
-
我已经在 VS2015 中运行了您的代码,针对 .NET Framework 4.5.2 进行了编译,这也产生了
NullReferenceException。所以它看起来并不孤立于 .NET 4.6 运行时。 -
如果我使用 VS2013 针对 4.5.2 或 4.6 进行编译,我不会得到
NullReferenceException。所以,就像你说的,这是一个编译器问题,而不是框架问题。 -
@Threadcreator:您的代码非常适合我。使用 Windows 10 专业版 (x64)、适用于 Windows 桌面的 Visual Studio 2015 Express。目标框架:4.5
-
是的,非常讨厌的 Roslyn 错误。有上千个,这个把 FUD 投入到 VB.NET 代码正确性上比较重。与this one 大致相同的错误。 DevDiv 不像以前那样做生意了,他们都喜欢新的敏捷方法,但测试不够敏捷。我们可以为他们做这项工作。 VS2013最先受到影响,VS2015是一大包'o bugs。更新 2 现在在 CTP 中,它可能修复了这个错误。不确定,我不想测试它:)
标签: .net vb.net visual-studio msbuild compiler-bug