VB.Net - "With"和Closures不混合

csa*_*uve 7 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
Run Code Online (Sandbox Code Playgroud)

抛出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
Run Code Online (Sandbox Code Playgroud)

请注意

$VB$Closure_ClosureVariable_7A_6.$VB$Local_VB$t_ref$L0 = Nothing 
Run Code Online (Sandbox Code Playgroud)

只有"问题"我真的可以问的是; 这是一个错误或一个奇怪的设计决定,由于某种原因,我没有看到.从现在开始,我几乎要避免使用"With".

Jar*_*Par 8

这种行为是"按设计",并且是由于该With陈述经常被误解的细节造成的.

With语句实际上将表达式作为参数而不是直接引用(即使它是最常见的用例之一).语言规范的第10.3节保证传递给With块的表达式仅被计算一次,并且可用于执行With语句.

这是通过使用临时实现的.因此,当.MemberWith语句中执行expressio 时,您不会访问原始值,而是指向原始值的临时值.它允许其他有趣的场景,如下所示.

Dim o as New Foo
o.bar = "some value"
With o   
  o = Nothing
  Console.WriteLine(.bar) ' Prints "some value"
End With
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为在With语句中你没有操作,o而是临时指向原始表达式.这个临时性只能保证在With声明的整个生命周期内都存在,因此Nothing最后会出现这种情况.

在您的示例中,闭包正确捕获临时值.因此,当它在With语句完成后执行时,临时是Nothing并且代码适当地失败.