Отложенное выполнение в VB.NET? - PullRequest
2 голосов
/ 26 января 2011
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, когда соединение недоступно, я хочу отложить загрузку, когда оно станет доступным.

Проблема в том, что приведенный выше код приводит к ошибке компилятора, поскольку a ByRef param нельзя использовать в лямбда-выражениях .

Есть идеи, как сделать это правильно?

Ответы [ 2 ]

3 голосов
/ 26 января 2011

Вы не можете использовать параметр ByRef в лямбда-выражениях, поскольку он может указывать на местоположение в стеке, которое больше не существует после того, как лямбда-выход освобождается.Все, что вам нужно сделать, это использовать более «постоянное» хранилище.Вы можете передать объект со свойством 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 # это трехэтапный процесс.Сначала вы должны объявить переменную для хранения лямбды и установить ее значение в ноль.Затем вы создаете лямбду, которая теперь может ссылаться на себя, чтобы отписаться.И, наконец, вы можете использовать лямбду, чтобы подписаться на событие.

0 голосов
/ 26 января 2011

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

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

  • Блокировать выполнение до завершения загрузки
  • Вызывать событие, когда загрузка завершается
  • Установить публично видимое поле в загрузчике, чтобы указать состояние загрузки

Один (возможный) компромисс - возвращение пользовательского объекта вместо 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

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

...