2010-10-29 4 views
7

Juste pensé que je partagerais ceci dans le cas où quelqu'un d'autre a couru dans ce domaine.
J'ai fait quelque chose de similaire aujourd'hui et il m'a fallu un certain temps pour comprendre pourquoi cela causait un problème à l'exécution.VB.Net - "Avec" et les fermetures ne se mélangent pas

Ce 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 

Lance une NullReferenceException. Il semble que With utilise la fermeture comme stockage temporaire, et à la fin, elle définit la variable de fermeture sur Nothing.

Voici ce code dans Redgate réflecteur:

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 

Notez que le

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

seulement "question" Je ne peux vraiment poser est; est-ce un bug ou une décision de conception étrange que, pour une raison quelconque, je ne vois pas. Je vais à peu près juste éviter d'utiliser "Avec" à partir de maintenant.

+0

haha, je viens de rencontrer ceci: D Je comprends maintenant pourquoi ça se passe mais c'est bizarre qu'il compile du tout ... –

Répondre

8

Ce comportement est "By Design" et résulte d'un détail souvent mal compris de l'instruction With. L'instruction With prend réellement une expression comme argument et non comme référence directe (même si c'est l'un des cas d'utilisation les plus courants). La section 10.3 de la spécification de langage garantit que l'expression passée dans un bloc With est évaluée une seule fois et est disponible pour l'exécution de l'instruction With.

Ceci est mis en œuvre en utilisant un temporaire. Ainsi, lors de l'exécution d'un .Member expressio à l'intérieur d'une instruction With, vous n'accédez pas à la valeur d'origine mais à une valeur temporaire qui pointe vers la valeur d'origine. Cela permet d'autres scénarios amusants tels que les suivants.

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

Cela fonctionne parce que dans l'instruction With vous ne travaillez pas sur o mais plutôt un pointage temporaire à l'expression originale. Ce temporaire est seulement garanti pour être vivant pendant la durée de la déclaration With et est donc Nothing à la fin.

Dans votre échantillon, la fermeture capture correctement la valeur temporaire. Par conséquent, lorsqu'il est exécuté après l'exécution de l'instruction With, le caractère temporaire est Nothing et le code échoue de manière appropriée.

+0

Je dirais que le code de csauve et l'exemple que vous donnez ne devraient pas être autorisés à compiler, point. –

+0

oh d'accord ça commence à avoir du sens.J'étais conscient du comportement dans votre exemple, je n'avais jamais essayé de faire une fermeture autour d'un. MERCI! – csauve

+0

Je sais que "Avec" crée un temporaire, mais je ne comprends pas pourquoi le temporaire est annulé. Si une fermeture contient une variable hors de portée, la variable ne restera-t-elle généralement pas valide? – supercat

1

Il n'y a vraiment qu'un bogue que je vois, le compilateur devrait générer une erreur pour cela. Ne devrait pas être difficile à mettre en œuvre. Vous pouvez le signaler à connect.microsoft.com

+1

Les Lambdas peuvent être valablement créées et utilisées dans un bloc 'With'. Une erreur entraînerait un code complètement valide entraînant une erreur. True Vb.Net a choisi un similaire pour capturer la variable d'itération dans une boucle 'foreach'. Cependant, dans ce cas, il y avait tellement d'exemples de personnes qui ont mal agi que nous avons estimé que c'était nécessaire. Nous avons choisi de le faire comme un avertissement mais les utilisateurs pouvaient le supprimer, ils croyaient * que l'utilisation était valide. – JaredPar

Questions connexes