【问题标题】:Delegates: How to make sense of them in VB.NET?代表们:如何在 VB.NET 中理解它们?
【发布时间】:2011-05-31 02:46:37
【问题描述】:

我希望尝试更好地理解代表。我查看了 MSDN 和其他各种站点上的示例,但我只是没有“理解”它们。我知道它们实际上类似于 C 中指向函数的指针。但出于某种原因,C 的语法在使用此类结构时更加清晰。

所以我开发了一个场景来尝试使用委托,或者至少在我认为这样的使用是有效的情况下。假设下面的代码属于某种类,并且MyObj 有一个 String 类型的 Name 属性,它返回与对象相同的小写名称(即Obj1.Name = "obj1"):

Private Shared MyList As New List(Of MyObj)(Obj1, Obj2, Obj3, Obj4, Obj5, Obj6)        

Private Shared Function FindObj(ByVal obj As MyObj, ByVal name As String) As Boolean
    Return String.Equals(obj.Name, name, OrdinalIgnoreCase)
End Function

Friend Shared Sub RedOctober()
    Dim obj4Pos As Int32 = -1

    For i As Int32 = 0 to (MyList.Count - 1) Step 1
        If FindObj(MyList(i), "obj4") Then
            obj4Pos = i
            Exit For
        End If            
    Next i

    If obj4Pos <> -1 Then
        Debug.Print("Found obj4!")
    Else
        Debug.Print("Couldn't find obj4! :(")
    End If
End Sub




这是您的基本 O(N)“在列表中搜索匹配的 thingamajig 并在找到时返回索引”。如果我使用FindIndex,我可以将其推断为“更好”的东西,但是:

Private Shared MyList As New List(Of MyObj)(Obj1, Obj2, Obj3, Obj4, Obj5, Obj6)        

Friend Shared Sub RedOctober()
    Dim obj4Pos As Int32 = MyList.FindIndex(
        Function(o) String.Equals(obj.Name, "obj4", OrdinalIgnoreCase))

    If obj4Pos <> -1 Then
        Debug.Print("Found obj4!")
    Else
        Debug.Print("Couldn't find obj4! :(")
    End If
End Sub




问题是,如果我想搜索的不仅仅是 obj4 怎么办?如果我以这种方式使用 FindIndex,我将需要一个专用的 lambda 表达式/匿名函数来处理我想要查找的 MyObj 的每个对象。这会在生成的二进制文件中添加额外的函数/子程序,它们每个都做大致相同的事情,所以它很臃肿。

如果我保留我的 FindObj 函数并以某种方式在委托中引用它,根据我想在 MyList 中找到的对象,将不同的字符串传递给它,这就是我知道委托可以使用的地方。问题是,FindIndex 想要一个 System.Predicate(Of T) 而我的 FindObj 函数有两个参数:检查 Name 属性的对象和检查它的字符串。

我的问题是:

  1. 这种情况适合代表吗?
  2. 是否会比直接使用 For 循环更快/更好/更高效/更清洁/选择自己的形容词?
  3. 这是否可行,而是通过纯 lambda 表达式,这样我可以传递我的两个 FindObj 参数并找到正确的对象,而无需声明多个具有相似性质的 lambda(因此会增加膨胀)。李>
  4. FindIndex 不是 Linq 的东西,但是有没有一种使用 Linq 的方法可以完成相同的任务,可能会更好(就效率而言——是的,我是一个优化狂,不,我不会为此道歉)?

VB.NET(嗯,一般来说是.NET)的游戏是,通常有多种方法来完成一项任务。困难的部分是找到适合特定情况的方法,不会不必要地臃肿或缓慢,并且对其他审查代码的人(或我在 2-3 个月的中断后)可读。

对于我怀疑的人来说,这应该是一件容易的事。如果我在示例中犯了任何错误,请随时指点并笑:)

【问题讨论】:

  • “委托”这个词只是函数指针的面向对象的名称。 “lambda”这个词只是内联定义的函数的函数式编程词。当您将 lambda 传递给函数时,您只是将地址传递给内联函数。

标签: vb.net


【解决方案1】:

要扩展您的第二个示例,您可以这样做:

Private Shared MyList As New List(Of MyObj)(Obj1, Obj2, Obj3, Obj4, Obj5, Obj6)        

Friend Shared Sub RedOctober(toFind as String)
    Dim obj4Pos As Int32 = MyList.FindIndex(
        Function(o) String.Equals(o.Name, toFind, OrdinalIgnoreCase))

    If obj4Pos <> -1 Then
        Debug.Print("Found " & toFind & "!")
    Else
        Debug.Print("Couldn't find " & toFind & "! :(")
    End If
End Sub

FindIndex 的参数是一个 lambda,它能够在声明时捕获范围内的变量。这使您可以“传入”要搜索的字符串,而无需将其作为匿名函数的参数。

委托基本上是对方法的引用。该方法可以是类中的成员方法、匿名函数或 lambda。 Predicate(of T) 只是一个预定义的委托类型,它将访问接受对方法或 lambda 的引用,以更适合上下文为准。

明确回答您的问题:

  1. Predicate(Of T) 只是一个预定义的委托类型。无论您传递给FindIndex(),都必须能够转换为这种类型。这可以是对方法或 lambda 的引用。

  2. 在这种情况下,可能不会。

  3. 参见上面的代码。

  4. FindIndexList(Of T) 上定义,这就是您在此处处理的内容。从理论上讲,它将针对 Linq 运算符可能不是的 List 实现进行优化。 Linq 代码最终看起来几乎相同,并且应该具有相似的性能,但如果您知道您使用的是 List,那么您最好还是像这里所做的那样坚持使用本机方法。

    李>

更新

我现在了解到您希望 RedOctober 使用您的 FindObj 方法。试试这个:

Friend Shared Sub RedOctober()
    Dim obj4Pos As Int32 = MyList.FindIndex(
        Function(o) FindObj(o, "obj4"))

    If obj4Pos <> -1 Then
        Debug.Print("Found obj4!")
    Else
        Debug.Print("Couldn't find obj4! :(")
    End If
End Sub

【讨论】:

  • 我可以,但RedOctober 并不是用来查找对象的方法。如果RedOctober 对班级做了很多其他事情怎么办?我在这里使用了Shared,但是如果它是一个实例方法并且做了类似验证类实例的内部一致性之类的事情呢?
  • 您仍然可以在 RedOctober 中声明一个局部变量来保存您要查找的字符串,然后在 lambda 中使用该变量。
  • @Andrew:我可以,但这个问题的重点是寻找基于委托的解决方案。这就是我学习的方式——采取一些我已经知道如何做得很好的事情,并通过一些我可能无法完全理解的方法来做不同的事情。这样,我可以更好地在两者之间进行翻译。我还没有在 MSDN 或 java2s 上找到与此匹配的示例。修改RedOctober,虽然完全有效,但不是我想要完成的。我基本上想保留我的 FindObj 函数,但以某种方式通过委托使用它,这样我就可以在我的项目中对两者进行基准测试。
  • 我想我现在明白了。您不能直接使用 FindObj,但可以将其包装在 lambda 中:Function(o) FindObj(o, "obj4")
  • 嗯,没想到。但它仍然会在 ILDASM 中为该格式的每次使用生成一个匿名函数 method _Lambda$__1。代表不应该是一种包装方式,这样我仍然可以使用FindObjFindIndex 并且不会在程序集中生成额外的函数吗?
猜你喜欢
  • 1970-01-01
  • 2023-03-08
  • 2011-06-03
  • 1970-01-01
  • 2011-02-22
  • 1970-01-01
  • 2011-04-25
  • 2011-01-27
  • 2017-07-09
相关资源
最近更新 更多