How to Use the Action and Predicate Delegates
August 23rd, 2007
How often do you write code to manually loop through a generic list or an array using a For…Each type loop? Have you written looping code to search for a particular property of a specific object in a list? Have you wondered if there was an easier way to select a sub-list from a generic list? Many programmers will use the manual loop method to achieve these goals. However, the Action and Predicate delegates offer you an easier and more flexible way to perform these operations.
Action
An Action is a delegate routine that works with a specified object. An Action doesn’t have a return value so it’s always declared as a Sub with a single parameter of a particular type. Pretty simple, but it may be a little confusing at first glance so we’ll work through a simple example.
We start with a generic list of products that have been passed into a routine. We need to load all of the products with a subcode specified by the user into a Listbox. Our ‘old’ way of doing this would look like this:
Public Sub LoadProductList(ByVal products As List(Of InventoryItem))
lstProducts.Items.Clear()
For Each item As InventoryItem In products
If cboProductSubCode.Text = item.SubCode Then
lstProducts.Items.Add(item)
End If
Next
End Sub
In the example above we manually loop through the List and load the Listbox. Now, here’s the code using an Action Delegate:
Public Sub LoadProductListBox(ByVal products As List(Of InventoryItem))
lstProducts.Items.Clear()
products.ForEach(AddressOf LoadListBoxItems)
End Sub
Public Sub LoadListBoxItems(ByVal item As InventoryItem)
If cboProductSubCode.Text = item.SubCode Then
lstProducts.Items.Add(item)
End If
End Sub
On the surface, it doesn’t seem like that much of a savings. We now have two routines instead of one, slightly larger, routine. This could impact readability of the code in some cases although it might help with more complex routines or if multiple ForEach calls were required. If we look at the performance, the Action method is slightly faster, by a factor of about 3 to 5 percent, so there is a tiny advantage there.
Where the real power of this method comes into play is where you need to pass a particular action into the routine depending upon other conditions. For example, we might want to have a different action for different types of inventory so we would pass our preferred loading routine as an Action as seen here:
......
If OnlyNewProducts Then
LoadProductListBox(InventoryList, New Action(Of InventoryItem)(AddressOf LoadImportedProducts))
Else
LoadProductListBox(InventoryList, New Action(Of InventoryItem)(AddressOf LoadAllProducts))
End If
......
Public Sub LoadProductListBox(ByVal products As List(Of InventoryItem), ByVal loadingAction As Action(Of InventoryItem))
lstProducts.Items.Clear()
products.ForEach(loadingAction)
End Sub
As you might guess, being able to pass in a delegate Action opens up a lot of possibilities.
Predicate
A Predicate is a delegate function that determines if a specified object meets a set of criteria. Like Action it has one argument, the particular type to be evaluated, and it also returns a Boolean, indicating if the object met or did not meet the selection conditions in the routine. Predicates are used in the Find functions of generic Lists. Let’s start by defining a set of criteria we want our InventoryItem objects to meet. In this case, we’re wanting products added in the past 30 days.
Function SearchForNewProducts(ByVal item As InventoryItem) As Boolean
If DateDiff(DateInterval.Day, item.IntroductionDate, Now) < 30 Then
Return True
Else
Return False
End If
End Function
Now we use the FindAll method to get a list of inventory items using this Predicate.
Dim NewProducts As List(Of InventoryItem) = ProductList.FindAll(AddressOf SearchForNewProducts) . . .
Another operation you might want to do is to see if a member of a list has a property that meets a unique criteria or you may want to just return the first or last matching item in the list. For that, you use the Find or FindLast functions. For our example, we’re going to find the first item that wildcard matches a user TextBox entry.
.....
Dim SelectedItem As InventoryItem = NewProducts.Find(AddressOf FindByDescription)
.....
Function FindByDescription(ByVal item As InventoryItem) As Boolean
If item.Description Like (txtItemDescription.Text & "*") Then
Return True
Else
Return False
End If
End Function
The other two Find methods are FindIndex and FindLastIndex. They return the index position of the object rather than the object itself. They also have overloads where you can specify the starting position and the number of objects in the list to search. This can be useful for incremental searches of large lists.
Another nice thing about using Predicates is that we can use the same delegate function for all of the Find functions:
..... Dim SelectedItem As InventoryItem = NewProducts.Find(AddressOf FindByDescription) ..... Dim SelectedItem As InventoryItem = NewProducts.FindLast(AddressOf FindByDescription) ..... Dim SelectedItem As List(Of InventoryItem) = NewProducts.FindAll(AddressOf FindByDescription) ..... Dim ItemIndex As Integer = ProductList.FindIndex(AddressOf FindByDescription) ..... Dim ItemIndex As Integer = ProductList.FindLastIndex(AddressOf FindByDescription)
As you can see, this can provide a lot of flexibility in your code.
I hope this article has helped you gain a better understanding of these powerful delegate methods. If you have any questions about them or anything you would like to add, please leave a comment.
Entry Filed under: VB.NET Tutorials
Rate This Article:









(9 votes, average: 4.67 out of 5)
6 Comments Add your own
1. Matt | September 6th, 2007 at 10:21 pm
I’ve been trying to figure out how to use delegates to check for a variable condition. For example, going back to the original code you used, say I want to only Load products whose .Text is equal to another variable. I can not figure out how to pass the second variable.
Take Fileter int he example below, for example:
Public Sub LoadProductList(ByVal products As List(Of InventoryItem), byval filter as string)
lstProducts.Items.Clear()
For Each item As InventoryItem In products
If cboProductSubCode.Text.startswith(filter) Then
lstProducts.Items.Add(item)
End If
Next
End Sub
2. jfrankcarr | September 7th, 2007 at 9:29 am
Good question Matt
You would need to make the extra value(s) you want to use in a Predicate function either module level variables or control values. The way Predicates work you can’t pass in additional values on the function call although it would be handy if you could.
Here’s a simple example using a filter value in a text box:
Public Function FilterProduct(ByVal product As InventoryItem) As Boolean If txtFilter.Text.Length = 0 Then Return True Else If product.Description Like String.Concat("*", txtFilter.Text, "*") Then Return True Else Return False End If End If End FunctionAnd a ListBox being populated with the result:
With lstSelectedProducts .DataSource = Nothing .DataSource = ProductList.FindAll(AddressOf FilterProduct) .DisplayMember = "Description" End WithI hope this answers your question. Let me know if it doesn’t.
3. Matt | September 7th, 2007 at 1:13 pm
Thanks, that definately answers my question and saves me from banging my head any more. Great info.
4. Omar | March 10th, 2008 at 8:25 am
Simply: thank you very much!
This is the clearest example I’ve found.
I solved my problem with few lines of code, and first of all in an elegant manner.
5. viktor | June 23rd, 2009 at 1:18 pm
You can shorten the predicate a bit by saying
Return item.Description Like (txtItemDescription.Text & “*”)
Instead of:
If item.Description Like (txtItemDescription.Text & “*”) Then
Return True
6. Boshmate | January 14th, 2010 at 6:11 am
I’ve beeen looking for a straightforward and to the point explanation of Predicates and this fiited the bill well ( and too I got Actions thrown in for free) . Also, the penny dropped in that these contrstructs are part of the push towards more functional style of programming and resonates strongly with work I’ve being doing with JQuery .. I kinda like this trend : it can make code more elegant. Anyway thanks for the clear concide article.
Leave a Comment
Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
Trackback this post | Subscribe to the comments via RSS Feed