VB.Net-“;加上;和闭包don';混合

VB.Net-“;加上;和闭包don';混合,vb.net,lambda,closures,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 Conso

我只是想和大家分享一下,以防有人碰到这件事。
我今天做了类似的事情,花了一段时间才弄明白为什么这会在运行时引起问题

此代码:

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

我只看到一个bug,编译器应该为此生成一个错误。实施起来应该不难。您可以在connect.microsoft.com上报告此行为

此行为是“精心设计”的,是由于对
With
语句的一个经常被误解的细节造成的

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

这是通过使用临时命令来实现的。因此,当在带有语句的
中执行
.Member
表达式时,您不是在访问原始值,而是在访问指向原始值的临时值。它允许其他有趣的场景,如以下

Dim o as New Foo
o.bar = "some value"
With o   
  o = Nothing
  Console.WriteLine(.bar) ' Prints "some value"
End With
这是因为在
With
语句中,您不是在
o
上操作,而是临时指向原始表达式。这个临时变量只保证在
With
语句的生存期内保持活动状态,因此在结尾处
没有任何内容


在您的示例中,闭包正确地捕获了临时值。因此,当它在
With
语句完成后执行时,临时值是
Nothing
,代码会相应地失败。

我想说,csauve的代码和您给出的示例都不应该被编译,句号。哦,好的,这开始有意义了。我知道你的例子中的行为,我只是从来没有尝试过用一个例子来结束。谢谢我知道“With”会创建一个临时值,但我不清楚为什么临时值会被置空。如果一个闭包包含一个超出范围的变量,那么该变量通常不会保持有效吗?@supercat,临时变量会被置空,因为它是临时变量,VB语言希望确保在使用临时变量的代码块完成后,该值可以被收集。临时语句确实被提升到闭包中,但语言服务仍然会将该值置零。相同的推理是否意味着在“if”语句中声明的变量在执行离开范围后将得到相同的处理?尽管可以肯定,我所看到的闭包让我想起了vb6糟糕的过去,在vb6中,参数默认为“ByRef”;有时,人们希望匿名方法使用其父级的局部变量,但对lambda中有时使用的任何局部变量进行堆分配(无论lambda是否执行)似乎不是一个好主意。lambda可以有效地创建并在
With
块中使用。在这里出错将导致完全有效的代码产生错误。True Vb.Net选择了类似的方法来捕获
foreach
循环中的迭代变量。然而,在这种情况下,有太多的人做错事的例子,我们认为这是必要的。我们确实选择这样做作为警告,虽然这样用户可以抑制它,他们认为使用是有效的。哈哈,我只是遇到了这样的情况:我现在明白为什么会发生这种情况,但它编译起来很奇怪。。。