以为我会分享这个以防万一其他人遇到这种情况 我今天做了类似的事情,花了一些时间来弄清楚为什么这会在运行时引起问题。
此代码:
Public Class foo
Public bar As String = "blah"
End Class
Public Sub DoInline()
Dim o As New foo
Dim f As Func(Of String)
With o
f = Function() .bar
End With
Try
Console.WriteLine(f.DynamicInvoke())
Catch ex As Reflection.TargetInvocationException
Console.WriteLine(ex.InnerException.ToString)
End Try
End Sub
抛出NullReferenceException。似乎With使用闭包作为其临时存储,并且在“End With”中,它将闭包的变量设置为Nothing。
以下是RedGate Reflector中的代码:
Public Shared Sub DoInline()
Dim o As New foo
Dim $VB$Closure_ClosureVariable_7A_6 As New _Closure$__1
$VB$Closure_ClosureVariable_7A_6.$VB$Local_VB$t_ref$L0 = o
Dim f As Func(Of String) = New Func(Of String)(AddressOf $VB$Closure_ClosureVariable_7A_6._Lambda$__1)
$VB$Closure_ClosureVariable_7A_6.$VB$Local_VB$t_ref$L0 = Nothing
Try
Console.WriteLine(RuntimeHelpers.GetObjectValue(f.DynamicInvoke(New Object(0 - 1) {})))
Catch exception1 As TargetInvocationException
ProjectData.SetProjectError(exception1)
Console.WriteLine(exception1.InnerException.ToString)
ProjectData.ClearProjectError
End Try
End Sub
注意
$VB$Closure_ClosureVariable_7A_6.$VB$Local_VB$t_ref$L0 = Nothing
只有“问题”我真的可以问;这是一个错误或一个奇怪的设计决定,由于某种原因,我没有看到。 从现在开始,我几乎要避免使用“With”。
答案 0 :(得分:8)
此行为是“按设计”,并且是With
语句经常被误解的详细信息的结果。
With
语句实际上将表达式作为参数而不是直接引用(即使它是最常见的用例之一)。语言规范的第10.3节保证传递到With
块的表达式仅被计算一次,并且可用于执行With
语句。
这是通过使用临时实现的。因此,当在.Member
语句中执行With
表达时,您不会访问原始值,而是指向原始值的临时值。它允许其他有趣的场景,如下所示。
Dim o as New Foo
o.bar = "some value"
With o
o = Nothing
Console.WriteLine(.bar) ' Prints "some value"
End With
这是有效的,因为在With
语句中你没有在o
上操作,而是临时指向原始表达式。此临时值仅保证在With
语句的生命周期内保持活动状态,因此最后Nothing
结束。
在您的示例中,闭包正确捕获临时值。因此,当它在With
语句完成后执行时,临时值为Nothing
且代码失败。
答案 1 :(得分:1)
我看到的确只有一个错误,编译器应该为此生成错误。不应该难以实施。您可以在connect.microsoft.com上报告