开发者

Delegates: How to make sense of them in VB.NET?

I am looking to try and understand delegates better. I've looked over the examples on MSDN and various other sites, but I just don't "get" them. I know that they are virtually similar to a pointer to a function in C. But for some reason, C's syntax is just SO much clearer on the use of such constructs.

So I've developed a scenario to try and make use of a delegate, or at least, where I think such a use is valid. Assume the below code is in a class of some kind and that MyObj has a Name property to it of type String that returns a lowercased name that is the same as the object (i.e., 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

This is your basic O(N) "Search a list for a matching thingamajig and return the index when found". I can extrapolate this into something a little "better" if I use FindIndex, however:

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

Problem is, what if I want to search for more than just obj4? If I use FindIndex that way, I'll need a dedicated lambda expression/anonymous function for each object of MyObj that I want to find. This adds extra functions/subs to the resulting binary that each do roughly the same thing, so it's bloat.

This is where I know delegates can be of use if I keep my FindObj function and somehow reference it in a delegate, passing it a different string dependeing on what object I want to find in MyList. Problem is, FindIndex wants a System.Predicate(Of T) whereas my FindObj function takes two arguments: the object to check the Name property on and the string to check it against.

My questions are thus:

  1. Is this an appropriate situation for a delegate?
  2. Is it going to be any quicker/better/more efficient/cleaner/pickyourownadjective than using a straight-up For loop?
  3. Is this doable instead via pure lambda expressions in such a way that I can pass my two FindObj arguments and have the correct object found w/o declaring multiple lambda's of similar nature (and thus, adding bloat).
  4. FindIndex isn't a Linq thing, but is there an approach using Linq that accomplishes the same task that may be better (in terms of efficiency -- yes, I am an optimization nut, and no, I will not apologize for it)?

The game with VB.NET (well, .NET in 开发者_运维技巧general) is that there are usually multiple ways to accomplish a task. The hard part is finding the way that suites a particular situation, is not unnecessarily bloaty or slow, and is readable to someone else reviewing the code (or me after a 2-3 month hiatus).

This should be an easy one for folks I suspect. And if I made any errors in my examples, feel free to point and laugh :)


To extend your second example you can do this:

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

The argument to FindIndex is a lambda which is able to capture variables that are in scope when it is declared. This enables you to "pass in" the string to search for without it being an argument to the anonymous function.

A Delegate is basically a reference to a method. That method can be a member method in a class, an anonymous function or a lambda. Predicate(of T) is just a predefined delegate type that will access accept a reference to a method or a lambda, whichever is better for the context.

To answer your questions explicitly:

  1. Predicate(Of T) is just a predefined delegate type. Whatever you pass to FindIndex() must be able to be converted to this type. That can be a reference to a method or a lambda.

  2. In this context, probably not.

  3. See the code above.

  4. FindIndex is defined on the List(Of T) which is what you're dealing with here. Theoretically it will be optimised for the List implementation which the Linq operators may not be. Linq code will end up looking pretty much the same, and should have similar performance, but if you know you're using a List then you're probably better off sticking to native methods as you have done here.

Update

I understand now that you want RedOctober to use your FindObj method. Try this:

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜