3

我正在尝试将一段代码从 VS2010 C# 4.0 框架转换为 VS2008 VB.NET 3.5 框架。我非常不确定如何进行,因为我不熟悉 VB.NET 和 VS2008,而且我发现这两种环境之间的差异令人沮丧。我已经从原始 C# 中删除了大部分不相关的代码,希望提供一个可以从中指出解决方案的问题的干净示例。

我确实尝试了一些代码转换器,但 lambdas 使它们绊倒了。

For Each我将第一个 lambda 转换为一个看起来不错的简单构造。然而,第二个 lambda(传递给ThreadPool.QueueUserWorkItem调用)让我不确定。由于 lambda 正在使用在taskcountlambda 主体之外声明的 var,因此我对使此代码对我的目标环境 (VS2008/VB.Net/3.5) 友好并保持代码尽可能相似的最佳方法犹豫不决原来的实现。除非没有其他方法,否则我不希望明确地将其推taskcount入。WaitCallback

public void sendAndBlock(List<ISendable> msgs)
{
    int taskCount = msgs.Count;
    AutoResetEvent fini = new AutoResetEvent(false);
    msgs.ForEach(m =>
    {
        ThreadPool.QueueUserWorkItem(nil =>
        {
            this.send(m);
            //threadsafe decrement and if we have no tasks left then unblock.
            if (0 == Interlocked.Decrement(ref taskCount))
            {
                fini.Set();//signal our main thread to proceed
            }
        }, m);
    });
    //block our main thread
    fini.WaitOne();
}
4

2 回答 2

4

不幸的是,与 Visual Studio 2008 一起使用的 VB 版本将要求您将其分解为三种方法(原始方法,每个 lambda 表达式加一种),因为旧版本的 VB.Net 尚不支持多行 lambda表达式。我们必须调整一些东西,以便我们可以将 lambda 表达式放入一行中,以正确关闭捕获的变量。如果您要使用相同版本的 Visual Studio,VB.Net 将能够以与 C# 相同的方式执行此操作(至少在这种情况下),因为 Visual Studio 2010 中包含的 VB.Net 版本确实支持多-line lambda 表达式。

我认为这会做到,但请检查一下:我在回复窗口中输入了所有这些内容,这很复杂,我可能有一两个错误。

Public Sub sendAndBlock(ByVal msgs As List(Of ISendable))

   Dim taskCount As Integer = msgs.Count
   Dim fini As New AutoResetEvent(False)

   'vb.net for vs2008 is limited to single-line lambdas.
   ' But we still need to create a closure to capture the fini and taskCount variables
   ' Means we can't translate old code line-for-line
   ' Solution is to abstract the old lambda expressions out to their own methds that we can call in a single line
   msgs.ForEach(Function(m) QueueMessage(m, fini, taskCount))

   fini.WaitOne()

End Sub

Private Sub QueueMessage(ByVal msg As ISendable, ByVal signaler As AutoRestEvent, ByRef taskCount As Integer)
    ThreadPool.QueueUserWorkItem(Function(n) SendQueuedMessage(msg, taskCount, signaler), msg);
End Sub

Private Sub SendQueuedMessage(ByVal msg As ISendable, ByRef TaskCount As Integer, ByVal signaler As AutoResetEvent)
    Me.send(msg)
    'Not sure about this line, because `Set` is a reserved word in VB
    If Interlocked.Decrement(taskCount) = 0 Then signaler.Set()
End Sub

我可能可以将其简化为两个方法而不是三个,因为其中一个额外的方法被压缩为一行,但为了简单和可读性,我将把它留在这里。

最后,如果仅能够从 Visual Studio 2010 定位 .Net 3.5 就足够了,那么您可以使用更接近原始版本的东西,因为您可以将 Visual Studio 2010 中更新的 VB 语言语法功能与 .Net 3.5 一起使用。

于 2013-05-06T20:28:52.337 回答
-1
Public Sub sendAndBlock(msgs As List(Of ISendable))
    Dim taskCount As Integer = msgs.Count
    Dim fini As New AutoResetEvent(False)
    msgs.ForEach(Function(m) 
    ThreadPool.QueueUserWorkItem(Function(nil) 
                                     Me.send(m)
                                     'threadsafe decrement and if we have no tasks left then unblock.
                                     If 0 = Interlocked.Decrement(taskCount) Then
                             'signal our main thread to proceed
                                     fini.[Set]()
                                     End If

                                 End Function, m)

                End Function)
    'block our main thread
    fini.WaitOne()
End Sub

谢谢,DeveloperFusion

于 2013-05-06T20:30:17.623 回答