我在VS2015.1上使用.NET 4.6.1在VB.NET 14中编写了以下WPF示例应用程序:
Class MainWindow
Public Sub New()
InitializeComponent()
End Sub
Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs)
MessageBox.Show("Pre")
Using window = New DisposableWindow()
window.Show()
For index = 1 To 1
Await Task.Delay(100)
Next
End Using
MessageBox.Show("Post")
End Sub
Class DisposableWindow
Inherits Window
Implements IDisposable
Public Sub Dispose() Implements IDisposable.Dispose
Me.Close()
MessageBox.Show("Disposed")
End Sub
End Class
End Class
以下示例产生以下输出:
这很奇怪。为什么Debug模式执行此代码的方式与Release模式不同??
当我将using块更改为手动try / finally块时,对window.Dispose()的调用甚至会抛出NullReferenceException:
Dim window = New DisposableWindow()
Try
window.Show()
For index = 1 To 1
Await Task.Delay(100)
Next
Finally
window.Dispose()
End Try
还有更奇怪的东西:当排除for循环时,样本完美无缺。我只让For-loop运行一次,以指定产生问题的最小循环量。也可以随意使用While循环替换For循环。它产生与For循环相同的行为。
作品:
Using window = New DisposableWindow()
window.Show()
Await Task.Delay(100)
End Using
现在你可能会想:'那很奇怪!'。情况变得更糟。 我也在C#(6)中做了完全相同的例子。所以在C#中,Debug和Release模式都会导致'Pre,Disposed,Post'作为输出。
样本可以在这里下载:
http://www.filedropper.com/vbsample
http://www.filedropper.com/cssample
我在这一点上很难过。这是.NET Framework的VB.NET堆栈中的错误吗?或者我是否想要完成一些奇怪的事情,运气似乎是C#中的工作,部分是VB.NET中的工作?
修改
做了更多测试:
更新
此后已经修复。版本1.2计划公开发布,但主分支中的最新版本应包含修复程序。
答案 0 :(得分:12)
我会写这个,这个Roslyn错误是非常讨厌的,并且可能打破很多VB.NET程序。以一种非常丑陋且难以诊断的方式。
这个bug很难看到,你必须用反编译器查看生成的程序集。我会以惊人的速度描述它。 Async Sub中的语句被重写为状态机,代码段中的特定类名是VB $ StateMachine_1_buttonClick。你只能用一个体面的反编译器来看它。此类的MoveNext()
方法执行方法体中的语句。在您的异步代码运行时,会多次输入此方法。
MoveNext()使用的变量需要捕获,将本地变量转换为类的字段。与您的window
变量一样,稍后当Using语句结束并且需要调用Dispose()方法时将需要它。 Debug版本中此变量的名称为$VB$ResumableLocal_window$0
。当您构建程序的Release版本时,编译器会尝试优化此类并且会严重错误地进行编译。它消除捕获并使window
成为MoveNext()的局部变量。这是非常错误的,当执行在Await
之后恢复时,该变量将是Nothing。因此,它的Dispose()方法不会被调用。
这个Roslyn错误具有非常大的影响,它将破坏在语句体包含Await的Async方法中使用Using
语句的任何VB.NET代码。这不容易诊断,丢失的Dispose()调用经常不被发现。除了像你这样的情况,它有一个非常明显的副作用。生产中必须有很多程序现在都有这个bug。副作用是他们会跑得很重,消耗的资源超过必要的资源。该程序可能会以许多难以诊断的方式失败。
此错误有一个临时解决方法,请务必永远不要部署VB.NET应用程序的Debug版本,这有其他问题。请关闭优化程序。选择发布版本并使用项目>属性>编译标签>高级编译选项>取消"启用优化"复选框。
哎呀,这很糟糕。