【发布时间】:2016-03-29 01:53:54
【问题描述】:
我在 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 循环运行一次,以指定产生问题的最小循环量。也可以随意用 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 版计划公开发布,但 master 分支中的最新版本应该包含修复。
【问题讨论】:
-
只是一种预感,但似乎 Dispose() 调用在不同的线程上。它在调试中起作用的原因是调试器正在为您切换线程。如果您添加了某种回调,将您切换回正确的线程,您可能会有更好的运气。
-
例如看代码如何使用Invoke required:stackoverflow.com/questions/3874134/…
-
这似乎是一个错误。我可以在我的 VS 2015 中复制它。无论如何,它可能已经在最新版本中修复(我没有最新版本)。如果错误仍然存在,您应该下载最后一个 VS 2015,您最好现在让 .NET 团队(在 github.com/dotnet/roslyn 创建票证),因为发布/调试中的不同行为看起来很丑。
-
又一个可怕的 Roslyn 错误。这是一个非常非常讨厌的方法,很容易根本不诊断。点击this web page上的“新问题”按钮报告错误。
标签: .net vb.net async-await