VB.Net - “With”和Closures不混合

时间:2010-10-29 18:38:40

标签: vb.net lambda closures

以为我会分享这个以防万一其他人遇到这种情况 我今天做了类似的事情,花了一些时间来弄清楚为什么这会在运行时引起问题。

此代码:

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”。

2 个答案:

答案 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上报告