2016-09-07 17 views
0

Withステートメントを使用してアクセスするときに、タスクで実行されるラムダ式のメンバ変数を参照するとNullReferenceExceptionがスローされることがわかりました。Withステートメントの式がタスクのラムダ式でNothingになる

たとえば、次のコードでコンソールに2行を出力するとします。最初のメンバーはobj.SomeStringSomeStringメンバーにアクセスし、2番目のメンバーはWithステートメントを使用し、.SomeStringでメンバーにアクセスします。私は両方のオプションが同等であることを期待しましたが、2番目のオプションは例外をスローします。

Class Foo 
    Public SomeString As String 
End Class 

Module Module1 

    Sub Main() 

     Dim obj As New Foo With {.SomeString = "Hello World"} 

     With obj 
      Task.Factory.StartNew(
       Sub() 
        Console.WriteLine("1:" + obj.SomeString) ' works 
        Console.WriteLine("2:" + .SomeString) ' NullReferenceException here 
       End Sub) 
     End With 

     Console.ReadKey() 

    End Sub 

End Module 

は私が With文に Console.ReadKey()文を移動すると、コードが動作します。

Withステートメントを使用せずに実際のコードを修正しましたが、私はここで何が欠けているのか分かりません。ラムダ式のobj変数のメンバーにアクセスできますが、With式のメンバーにアクセスできないのはなぜですか?例外がスローされたときに私はデバッガでそれを見ることができるので、ガベージコレクションは行われていません。式はスコープ(またはそのようなもの)から外れているように見えますが、コンパイラは私が期待したとおりにして、それをobjと同じように扱わないのはなぜですか?

答えて

2

VBコンパイラがブロックとラムダ式Withをサポートするのは、ブードゥーのためです。 RedgateのReflectorのようなデコンパイラを使ってコードを見ると、コードは次のようなコードに変換されます。ただし、VBでサポートされている変数に名前を変更しました。彼らは非常に長くなると、あなたはコンパイラがWith変数とラムダ式のメソッドへの参照を保持するクラスを作成していることがわかりますVBの変数名

<STAThread> _ 
Public Shared Sub Main() 
    Dim var1 As New GeneratedClass1 
    Dim foo As New Foo With {.SomeString = "Hello World"} 
    var1.objVar = foo 

    Dim var2 As New GeneratedClass1.GeneratedClass2 With {.var2 = var1, .theWithVariable = var1.objVar} 
    Task.Factory.StartNew(New Action(AddressOf var2._Lambda___1)) 
    var2.theWithVariable = Nothing 
    Console.ReadKey() 
End Sub 

<CompilerGenerated> _ 
Friend Class GeneratedClass1 
    ' Methods 
    <DebuggerNonUserCode> _ 
    Public Sub New() 
    End Sub 

    <DebuggerNonUserCode> _ 
    Public Sub New(ByVal other As GeneratedClass1) 
      If (Not other Is Nothing) Then 
       Me.objVar = other.objVar 
      End If 
    End Sub 


    ' Fields 
    Public objVar As Foo 

    ' Nested Types 
    <CompilerGenerated> _ 
    Friend Class GeneratedClass2 
      ' Methods 
      <DebuggerNonUserCode> _ 
      Public Sub New() 
      End Sub 

      <DebuggerNonUserCode> _ 
      Public Sub New(ByVal other As GeneratedClass2) 
       If (Not other Is Nothing) Then 
        Me.theWithVariable = other.theWithVariable 
       End If 
      End Sub 

      <CompilerGenerated> _ 
      Public Sub _Lambda___1() 
       Console.WriteLine(("1:" & Me.var2.objVar.SomeString)) 
       Console.WriteLine(("2:" & Me.theWithVariable.SomeString)) 
      End Sub 


      ' Fields 
      Public theWithVariable As Foo 
      Public var2 As GeneratedClass1 
    End Class 
End Class 

には無効な文字を含めることができます。 With変数が有効範​​囲外になるとすぐに、Nothingに設定され、タスクが実行されるときにnull参照式が設定されます。