【问题标题】:VB.NET Iterator Function Loses Local VariablesVB.NET 迭代器函数丢失局部变量
【发布时间】: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


【解决方案1】:

首先,感谢您关注我向 Roslyn 团队提出的问题。

我从https://github.com/dotnet/roslyn(master 分支)提取了最新的 Roslyn 源代码,并在 BasicCompilerEmitTest 项目中添加了一个额外的单元测试,如下所示:

Imports Microsoft.CodeAnalysis.VisualBasic.UnitTests

Public Class KirillsTests
  Inherits BasicTestBase

  <Fact>
  Public Sub IteratorVariableCaptureTest()
    Dim source =
<compilation name="Iterators">
  <file name="a.vb">
Imports System
Imports System.Collections.Generic

Module Module1

    Sub Main()
        For Each o As Integer In GetAllStuff()
            Console.WriteLine(o.ToString())
        Next

        Console.WriteLine("done")
    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
  </file>
</compilation>

    Dim expectedOutput = <![CDATA[1
done]]>

    Dim compilation = CompilationUtils.CreateCompilationWithReferences(source, references:=LatestVbReferences, options:=TestOptions.DebugExe)
    CompileAndVerify(compilation, expectedOutput:=expectedOutput)
    CompileAndVerify(compilation.WithOptions(TestOptions.ReleaseExe), expectedOutput:=expectedOutput)
  End Sub

End Class

由于使用了 XElementXCData,这可能看起来很复杂,但这是其他 Roslyn 单元测试使用的格式。

我只对您在问题中发布的代码进行了一项更改 - 将 Console.ReadKey() 替换为 Console.WriteLine("done") 以便我可以跟踪成功完成(因为 CompileAndVerify 只是忽略异常)。

以上测试通过。在map.Values访问上没有NullReferenceException,输出为:

1 完毕

...正如预期的那样。因此,您的错误似乎已得到修复 - 尽管我无法确定该修复是否会随 Visual Studio 2015 Update 2 一起提供。

async variable capture issue 已由 pull request #7693 修复,但 DataFlowPass.SetSlotUnassigned 已被重写(拆分为 2 个方法并进行了修改),因此我无法确认您发现的迭代器问题是由该特定拉取请求修复,还是由其他一些代码更改。

【讨论】:

  • 更新 2 CTP 确实解决了这个问题。我向 Microsoft 支持的朋友发送了电话,以获取有关特定修复程序的更多详细信息,并将使用我提供的任何信息再次更新。可悲的是,2015/4.6 感觉就像一个漫长的 CTP。平台稳定性曾经是 .NET 的一大卖点……
  • @roken,很高兴知道修复程序很快就会发布。 Roslyn 仍处于早期阶段,你是对的,它确实有点实验性的感觉。从很多方面来看,这是一个激动人心的时刻,但并不总是像这个问题所展示的那样有充分的理由。
猜你喜欢
  • 1970-01-01
  • 2012-12-24
  • 2016-09-02
  • 2020-11-22
  • 2012-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-02
相关资源
最近更新 更多