VB.Net - “与”和闭包不能混用

发布于 2024-09-30 00:29:34 字数 1456 浏览 2 评论 0原文

只是想我会分享这个以防其他人遇到这个问题。
我今天做了类似的事情,我花了一段时间才弄清楚为什么这会在运行时导致问题。

此代码:

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

Just thought I'd share this in case anyone else has run into this.
I did something similar today and it took me a while to figure out why this was causing a problem at runtime.

This code:

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

Throws a NullReferenceException. It seems as though the With is using the closure as its temp storage, and at the "End With", it sets the closure's variable to Nothing.

Here is that code in 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

Notice the

$VB$Closure_ClosureVariable_7A_6.$VB$Local_VB$t_ref$L0 = Nothing 

Only "question" I can really ask is; is this a bug or a strange design decision that for some reason I'm not seeing.
I'm pretty much just going to avoid using "With" from now on.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我的影子我的梦 2024-10-07 00:29:34

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

With 语句实际上将表达式作为参数而不是直接引用(即使它是最常见的用例之一)。语言规范第 10.3 节保证传递到 With 块的表达式仅计算一次,并且可用于执行 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 语句的生命周期内保持活动状态,因此最终不会 Nothing 被删除。

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

This behavior is "By Design" and results from an often misunderstood detail of the With statement.

The With statement actually takes an expression as an argument and not a direct reference (even though it's one of the most common use cases). Section 10.3 of the language spec guarantees that the expression passed into a With block is evaluated only once and is available for the execution of the With statement.

This is implemented by using a temporary. So when executing a .Member expressio inside a With statement you are not accessing the original value but a temporary which points to the original value. It allows for other fun scenarios such as the following.

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

This works because inside the With statement you are not operating on o but rather a temporary pointing to the original expression. This temporary is only guaranteed to be alive for the lifetime of the With statement and is hence Nothingd out at the end.

In your sample the closure correctly captures the temporary value. Hence when it's executed after the With statement completes the temporary is Nothing and the code fails appropriately.

眉目亦如画i 2024-10-07 00:29:34

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

There's really only one bug that I see, the compiler should generate an error for this. Shouldn't be hard to implement. You can report it at connect.microsoft.com

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文