我在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
下面的示例生成以下输出:
调试模式:Pre,Disposed,Post
发布模式:Pre,Post
这很奇怪.为什么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中?
编辑:
做了一些测试:
在VB.NET中禁用发布模式的编译器优化,使其行为类似于调试模式(正如预期的那样,但想要测试它,以防万一).
当我定位.NET 4.5(async/await可用的最早版本)时,也会出现这个问题.
更新:
这已经得到修复.版本1.2计划公开发布,但主分支中的最新版本应包含修复程序.
请参阅:https://github.com/dotnet/roslyn/issues/7669
我会写这个,这个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错误具有非常大的影响,它会破坏Using
在语句体包含Await 的Async方法中使用该语句的任何VB.NET代码.这不容易诊断,丢失的Dispose()调用经常不被发现.除了像你这样的情况,它有一个非常明显的副作用.生产中必须有很多程序现在都有这个bug.副作用是它们会"重"运行,消耗的资源超过必要的资源.该程序可能以许多难以诊断的方式失败.
这个bug有一个临时的解决方法,一定要永远不要部署你的VB.NET应用程序的Debug版本,这有其他问题.请关闭优化程序.选择Release构建并使用Project> Properties> Compile选项卡> Advanced Compile Options>取消选中"Enable optimizations"复选框.
哎呀,这很糟糕.