2011-01-26 3 views
2
Private Sub LoadData(Of T)(ByVal query As ObjectQuery(Of T), 
    ByRef result As IEnumerable(Of T)) 
    If Connection.State = ConnectionState.Open Then 
    result = query.ToArray 
    Else 
    AddHandler Connection.StateChange, 
     Sub(sender As Object, e As StateChangeEventArgs) 
     LoadData(query, result) 
     End Sub 
    End If 
End Sub 

В приведенном выше коде я пытаюсь восстановить функцию LoadData, когда соединение недоступно, я хочу отложить загрузку до того момента, когда она станет доступной.Отложенное исполнение в VB.NET?

Проблема в приведенном выше коде приводит к ошибке компилятора, поскольку a ByRef param cannot be used in lambda expressions.

Любая идея, как это сделать правильно?

+0

Итак, что происходит, пока вы ждете открытия соединения? – ChaosPandion

+0

Я не думаю, что слово «recuse» означает то, что вы думаете, что это значит. – Gabe

+0

@ChaosPandio: к этому времени выполняются разные запросы: состояние подключения - «ConnectionState.Connecting» и т. Д., Я жду, когда соединение станет «открытым» или, по крайней мере, «закрыто», поэтому я могу явно открыть его, сценарий заключается в том, что соединение задействовано, а LoadData вызывается другим потоком. @Gabe: Я имел в виду рекурсию @ – Shimmy

ответ

3

Вы не можете использовать параметр ByRef в лямбда, потому что он может указывать на местоположение в стеке, которое больше не существует после выполнения лямбда-execuetes. Все, что вам нужно сделать, это использовать более «постоянное» место хранения. Вы можете передать объект с свойством IEnumerable(Of T), который вы можете установить, чтобы назначить результат.

Вероятно, лучшим вариантом является передача делегату (Action<IEnumerable<T>>), который принимает результат и выполняет любое действие, которое требует вызывающий объект с результатом. Вот пример в C#:

void LoadData<T>(ObjectQuery<T> query, Action<IEnumerable<T>> action) 
{ 
    if (Connection.State == ConnectionState.Open) 
     action(query.ToArray()); 
    else 
    { 
     // create a lambda to handle the next state change 
     StateChangeEventHandler lambda = null; 
     lambda = (sender, e) => 
     { 
      // only perform our action on transition to Open state 
      if (Connection.State == ConnectionState.Open) 
      { 
       // unsubscribe when we're done 
       Connection.StateChange -= lambda; 
       action(query.ToArray()); 
      } 
     } 
     // subscribe to connection state changes 
     Connection.StateChange += lambda; 
    } 
} 

И вы бы вызвать LoadData так:

LoadData(query, results => listBox.DataSource = results); 

Обратите внимание на нюансы моей реализации. Например, он не вызывает себя в обработчике событий, потому что это заставит его повторно подписываться на событие, если обработчик когда-либо вызывается с состоянием, отличным от Open. Он также отменяет подписку на событие после открытия соединения. Я не уверен, как это перевести на VB, но на C# это трехэтапный процесс. Сначала вы должны объявить переменную для хранения лямбда и установить ее значение в значение null. Затем вы создаете лямбду, которая теперь может ссылаться на отмену подписки. И, наконец, вы можете использовать лямбда, чтобы подписаться на это событие.

+0

Можете ли вы предоставить некоторый код (C# также будет работать) – Shimmy

+0

@Shimmy: Я добавил пример в C# – Gabe

+1

Событие Подписывание/отмена подписки выполняется с помощью 'AddHandler' и' RemoveHandler' в VB – MarkJ

0

У вас есть проблема в том, что ваш вызывающий поток не имеет ни малейшего представления, если переменная была заселена LoadData() вызова

В Тхи случае вам нужно сделать что-то вроде:

  • исполнение блока пока нагрузка не завершит
  • Поднять событие, когда нагрузка завершает
  • Установите общедоступное поле на заряжающего для указания состояния нагрузки

One (возможно) компромисс будет возвращать пользовательский объект вместо IEnumerable

пользовательский объект может сразу же попытаться загрузить данные и держать повторно пробовать, пока успех. Если результирующий набор пользовательского объекта считывается до того, как возникла нагрузка, заблокируйте нить до завершения загрузки, в противном случае верните результирующий набор

В этом случае вы получаете выгоду, если есть задержка между загрузкой и используемые данные - ваша программа может продолжаться до тех пор, пока не потребуются данные. Является ли это я полезным или нет, полностью зависит от того, для чего вы его используете.

Более подробная информация о блокировке исполнения: Это зависит немного от того, как стало известно, что соединение обратно, но что-то вроде:

Public Sub LoadData(Of T)(ByVal query As ObjectQuery(Of T), ByRef result As IEnumerable(Of T)) 
    While Not Connection.State = ConnectionState.Open 
    Threading.Thread.Sleep(100) 'Pick a decent value for this delay based on how likely it is the connection will be available quickly 
    End While 
    result = 'Use the connection to get your data 
End Sub 

Есть ли какая-либо причина вы делаете это как sub с параметрами ByRef в отличие от функции? Вы только «возвращаете» один объект, поэтому я не совсем понимаю его преимущества. Не то, чтобы это имело огромное значение функционально, но было бы более читаемым ...

+0

можете ли вы подробнее рассказать о части 1 «Выполнение блока»? – Shimmy

+0

@Shimmy Я отредактировал свой ответ – Basic

Смежные вопросы