2010-10-29 5 views
7

Ich dachte nur, ich würde dies teilen, falls irgendjemand anderes in diese Sache geraten ist.
Ich habe heute etwas ähnliches gemacht und es hat eine Weile gedauert, bis ich herausgefunden habe, warum dies zur Laufzeit ein Problem verursacht hat.VB.Net - "With" und Closures vermischen sich nicht

Dieser 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 

wirft eine Nullreferenceexception. Es sieht so aus, als ob With den Closure als temporären Speicher verwendet und bei "End With" die Closure-Variable auf Nothing setzt.

ist hier, dass Code in RedGate Reflektor:

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 

Hinweis der

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

Nur "Frage" ich wirklich fragen kann; Ist das ein Fehler oder eine seltsame Designentscheidung, die ich aus irgendeinem Grund nicht sehe? Ich werde von jetzt an einfach vermeiden, "With" zu verwenden.

+0

haha, ich bin gerade auf folgendes gestoßen: D Ich verstehe jetzt, warum es passiert, aber es ist komisch, dass es überhaupt kompiliert ... –

Antwort

8

Dieses Verhalten ist "By Design" und Ergebnisse aus einem oft missverstandenen Detail der With-Anweisung.

Die Anweisung With nimmt einen Ausdruck tatsächlich als Argument und nicht als direkte Referenz (obwohl es einer der häufigsten Anwendungsfälle ist). Abschnitt 10.3 der Sprachspezifikation garantiert, dass der in einen With-Block übergebene Ausdruck nur einmal ausgewertet wird und für die Ausführung der With-Anweisung verfügbar ist.

Dies wird mit einem temporären implementiert. Wenn Sie also eine .Member expressio in einer With-Anweisung ausführen, greifen Sie nicht auf den ursprünglichen Wert, sondern auf eine temporäre, die auf den ursprünglichen Wert verweist. Es ermöglicht andere lustige Szenarien wie die folgenden.

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

Das funktioniert, weil in der With Anweisung Sie sind nicht auf o sondern eine temporäre zeigt auf den ursprünglichen Ausdruck arbeitet. Dieses temporäre ist nur garantiert für die Lebenszeit der With Anweisung am Leben und ist daher am Ende Nothing d out.

In Ihrem Beispiel erfasst der Abschluss korrekt den temporären Wert. Daher, wenn es ausgeführt wird, nachdem die With-Anweisung abgeschlossen ist, ist das temporäre Nothing und der Code schlägt entsprechend fehl.

+0

Ich würde sagen, sowohl csauve's Code und das Beispiel, das Sie geben, sollte nicht erlaubt sein, zu kompilieren, Punkt. –

+0

oh okay, das fängt an Sinn zu machen.Ich war mir des Verhaltens in Ihrem Beispiel bewusst, ich hatte nie versucht, eine Schließung um ein mit zu machen. VIELEN DANK! – csauve

+0

Ich weiß, dass "With" eine temporäre erstellt, aber ich bin nicht klar, warum die temporäre Nulled wird. Wenn eine Closure eine Variable enthält, die außerhalb des Gültigkeitsbereichs liegt, bleibt die Variable normalerweise gültig? – supercat

1

Es gibt wirklich nur einen Fehler, den ich sehe, der Compiler sollte einen Fehler dafür erzeugen. Sollte nicht schwer zu implementieren sein. Sie können es unter connect.microsoft.com melden

+1

Lambda kann gültig erstellt und in einem 'With' Block verwendet werden. Ein Fehler würde dazu führen, dass ein vollständig gültiger Code zu einem Fehler führt. True Vb.Net wählte ein ähnliches zur Erfassung der Iterationsvariablen in einer "foreach" -Schleife. Aber in diesem Fall gab es so viele Beispiele von Leuten, die es falsch machten, dass wir es für notwendig hielten. Wir haben uns jedoch dafür entschieden, dies als Warnung zu tun, damit Benutzer sie unterdrücken konnten, wenn sie glaubten, dass die Verwendung gültig war. – JaredPar