VB.Net - «С» и замыкания не смешиваются - PullRequest
7 голосов
/ 29 октября 2010

Просто подумал, что поделюсь этим на случай, если кто-нибудь еще столкнется с этим.
Сегодня я сделал нечто подобное, и мне потребовалось некоторое время, чтобы понять, почему это вызывает проблемы во время выполнения.

Этот код:

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

Выдает исключение NullReferenceException. Похоже, что With использует замыкание в качестве своего временного хранилища, а в «End With» он устанавливает переменную замыкания в Nothing.

Вот этот код в 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

Обратите внимание на

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

Единственный «вопрос», который я действительно могу задать, это; это ошибка или странное дизайнерское решение, которое по какой-то причине я не вижу. Теперь я просто буду избегать использования «С».

Ответы [ 2 ]

8 голосов
/ 29 октября 2010

Это поведение "По замыслу" и является результатом часто неправильно понятой детали оператора With.

Оператор With фактически принимает выражение в качестве аргумента, а не прямую ссылку (хотя это один из самых распространенных вариантов использования). Раздел 10.3 спецификации языка гарантирует, что выражение, переданное в блок With, вычисляется только один раз и доступно для выполнения оператора With.

Это реализовано с использованием временного. Таким образом, при выполнении выражения .Member в выражении With вы обращаетесь не к исходному значению, а к временному, который указывает на исходное значение. Это позволяет для других забавных сценариев, таких как следующие.

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

Это работает, потому что внутри оператора With вы работаете не с o, а с временным указанием на исходное выражение. Это временное действие гарантированно будет действительным только в течение жизни оператора With и, следовательно, в конце будет Nothing d.

В вашем примере замыкание правильно фиксирует временное значение. Следовательно, когда он выполняется после завершения оператора With, временное значение равно Nothing, и код соответствующим образом завершается с ошибкой.

1 голос
/ 29 октября 2010

На самом деле, я вижу только одну ошибку, компилятор должен генерировать ошибку для этого.Не должно быть сложно реализовать.Вы можете сообщить об этом на connect.microsoft.com

...