2010-10-29 20 views
7

Solo pensé en compartir esto en caso de que alguien más se haya topado con esto.
Hice algo similar hoy y me llevó un tiempo descubrir por qué esto causaba un problema en el tiempo de ejecución.VB.Net - "Con" y cierres no mezclar

Este código:

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 

Lanza una NullReferenceException. Parece que el With está utilizando el cierre como su almacenamiento temporal, y en el "End With", establece la variable del cierre en Nothing.

aquí es que el código en 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 

Aviso la "cuestión"

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

Sólo puedo pedir es realmente; ¿Es este un error o una extraña decisión de diseño que por alguna razón no estoy viendo? Simplemente voy a evitar usar "Con" a partir de ahora.

+0

jaja, acabo de encontrarme con esto: D Ahora entiendo por qué está sucediendo, pero es raro que se compile en absoluto ... –

Respuesta

8

Este comportamiento es "Por diseño" y resulta de un detalle a menudo incomprendido de la declaración With.

La declaración With en realidad toma una expresión como argumento y no como referencia directa (aunque es uno de los casos de uso más comunes). La Sección 10.3 de la especificación de idioma garantiza que la expresión pasada a un bloque With se evalúa solo una vez y está disponible para la ejecución de la declaración With.

Esto se implementa mediante el uso de un temporal. Por lo tanto, al ejecutar una expressio .Member dentro de una declaración With, no está accediendo al valor original sino a un valor temporal que apunta al valor original. Permite otros escenarios divertidos como los siguientes.

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

Esto funciona porque dentro de la instrucción With no se esté usando el o sino más bien un señalador temporal a la expresión inicial. Este temporal solo está garantizado para estar vivo durante la vigencia de la declaración With y, por lo tanto, es Nothing d out al final.

En su muestra, el cierre captura correctamente el valor temporal. Por lo tanto, cuando se ejecuta después de que la instrucción With completa el temporal es Nothing y el código falla adecuadamente.

+0

Diría que tanto el código de csauve como el ejemplo que das no deberían poder compilarse, punto. –

+0

oh bien, está empezando a tener sentido.Era consciente del comportamiento en tu ejemplo, simplemente nunca había intentado cerrarlo. ¡GRACIAS! – csauve

+0

Sé que "Con" crea un temporal, pero no tengo claro por qué se anula el temporal. Si un cierre contiene una variable que está fuera del alcance, ¿no será la variable usualmente válida? – supercat

1

Realmente solo hay un error que veo, el compilador debería generar un error para esto. No debería ser difícil de implementar. Puede informarlo en connect.microsoft.com

+1

Lambdas puede crearse válidamente y usarse dentro de un bloque 'With'. Si se produce un error aquí, el código completamente válido dará como resultado un error. True Vb.Net eligió un similar para capturar la variable de iteración en un bucle 'foreach'. Sin embargo, en ese caso había tantos ejemplos de personas que lo estaban haciendo mal que sentimos que era necesario. Sin embargo, elegimos hacerlo como advertencia para que los usuarios pudieran suprimirlo, * creían * que el uso era válido. – JaredPar