Link Round-Up for 8/23/07Introduction To VB.NET Delegates

How to Use the Action and Predicate Delegates

August 23rd, 2007

Delegates are an important tools for drafting your applicationsHow 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.

Share This Article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati
  • DotNetKicks
  • DZone

Entry Filed under: VB.NET Tutorials


Rate This Article:

Not That GoodCould Be BetterOKGoodGreat (9 votes, average: 4.67 out of 5)
Loading ... Loading ...

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 Function
    

    And a ListBox being populated with the result:

    With lstSelectedProducts
            .DataSource = Nothing
            .DataSource = ProductList.FindAll(AddressOf FilterProduct)
            .DisplayMember = "Description"
    End With
    

    I 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

Required

Required, hidden

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


Visit Me At My New Site, Programming In C#

Most Popular Articles

Highest Rated Articles

Categories

Most Recent Articles

Feeds

 Subscribe in a reader

To subscribe by e-mail
Enter your address here

Delivered by FeedBurner

VB Opportunities

Archives