VB.NET 中的延迟执行?

发布于 2024-10-14 07:03:26 字数 632 浏览 6 评论 0 原文

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< /code> param 不能在 lambda 表达式中使用

知道如何以正确的方式做到这一点吗?

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

In the above code I am trying to recurse the LoadData function when the connection is unavailable, I want to defer the loading to when it becomes available.

The problem is the above code leads to a compiler error, since a ByRef param cannot be used in lambda expressions.

Any idea of how to do this the right way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

初雪 2024-10-21 07:03:26

您不能在 lambda 中使用 ByRef 参数,因为它可能指向堆栈上的某个位置,而该位置在 lambda 执行后就不再存在。您所要做的就是使用更“永久”的存储位置。您可以传入一个具有 IEnumerable(Of T) 属性的对象,您可以设置该属性以分配结果。

一个可能更好的选择是传入一个委托 (Action>),该委托接受结果并执行调用者对结果所需的任何操作。下面是一个 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# 中,这是一个 3 步过程。首先,您必须声明一个变量来保存 lambda 并将其值设置为 null。然后您创建 lambda,它现在可以引用自身来取消订阅。最后,您可以使用 lambda 来订阅事件。

You cannot use a ByRef parameter in a lambda because it could be pointing to a location on the stack which no longer exists once the lambda execuetes. All you have to do is use a more "permanent" storage location. You can pass in an object with a property of IEnumerable(Of T) that you can set in order to assign the result.

A probably better option is to pass in a delegate (Action<IEnumerable<T>>) that accepts the result and performs whatever action the caller requires with the result. Here's an example in 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;
    }
}

And you would invoke LoadData like this:

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

Note the nuances of my implementation. For example, it does not call itself within the event handler because that will cause it to resubscribe to the event if the handler is ever called with a state other than Open. It also unsubscribes from the event once the connection opens. I'm not sure how this would translate to VB, but in C# this is a 3-step process. First you must declare a variable to hold the lambda and set its value to null. Then you create the lambda, which can now reference itself to unsubscribe. And finally you can use the lambda to subscribe to the event.

戒ㄋ 2024-10-21 07:03:26

您遇到的问题是,您的调用线程不知道该变量是否已由 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 参数的子程序而不是函数来执行此操作?你只“返回”一个对象,所以我不太明白这样做的好处。并不是说它会在功能上产生巨大的差异,但它会更具可读性......

You've got a problem in that your calling thread has no idea if the variable has been populated by the LoadData() call

In thi case you need to do something like:

  • Block execution until the load completes
  • Raise an event when the load completes
  • Set a publicly visible field on your loader to indicate load status

One (possible) compromise would be to return a custom object instead of an IEnumerable

The custom object could immediately attempt to load the data and keep re-trying until success. If the result set of the custom object is read before a load has occured, block the thread until the load completes, otherwise return the result set

In this scenario, you get a benefit if there's a delay between the Load occuring and the data being used - your program can continue on until it needs the data. Whether this i useful or not depends entirely on what you're using it for.

More information on blocking execution:
It depends a little on how you become aware that the connection is back up but something like:

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

Is there any reason you're doing this as a sub with ByRef parameters as opposed to a function? You're only "returning" one object so I don't quite see the benefit. Not that it would make a huge difference functionally but it would be more readable...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文