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.
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 Nothing
d 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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With