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:
- Is this an appropriate situation for a delegate?
- Is it going to be any quicker/better/more efficient/cleaner/pickyourownadjective than using a straight-up
For
loop? - 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). 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:
Predicate(Of T)
is just a predefined delegate type. Whatever you pass toFindIndex()
must be able to be converted to this type. That can be a reference to a method or a lambda.In this context, probably not.
See the code above.
FindIndex
is defined on theList(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
精彩评论