Oddly enough there is nothing really new here. However, most of the documentation I’ve been sifting through has all been C# related. Since I had to code this up using vb.net at work, I thought it would be helpful to have a reference available for others out there in vb.net land.
Lately I’ve really fallen in love with the power that generics, List(Of T) and the like, provide for my day to day operations. [Yes, I know this came out in .Net 2.0 but things run a little slow over here...] What I especially like to do is create my own custom objects and maintain them in a List collection for easy iteration and retrieval. Recently I created user information object that I use for somebody logged in as part of the data model. I created a List(Of UserInfo) and could add/remove/list these users without hassle. However, I also needed to sort this list from time to time. In addition, I would need to find an individual user to display particular data about them on a page. Doing this took a small amount more work, but is quick and easy once you know what to do.
UserInfo Object
For starters, here is the object I’ve created to store user information (property/method information has been removed for brevity):
Public Class UserInfo
' Private Members
Private m_UserId As String
Private m_FirstName As String
Private m_LastName As String
Private m_SecurityLevel As Integer
Private m_Roles As List(Of String)
' Public Properties
Public Property UserId() As String
Get
Return m_UserId
End Get
Set(ByVal value As String)
value = m_UserId
End Set
End Property
Public Property FirstName() As String
Get
Return m_FirstName
End Get
Set(ByVal value As String)
m_FirstName = value
End Set
End Property
Public Property LastName() As String
Get
Return m_LastName
End Get
Set(ByVal value As String)
m_LastName = value
End Set
End Property
Public Property SecurityLevel() As Integer
Get
Return m_SecurityLevel
End Get
Set(ByVal value As Integer)
m_SecurityLevel = value
End Set
End Property
Public Property Roles() As List(Of String)
Get
Return m_Roles
End Get
Set(ByVal value As List(Of String))
m_Roles = value
End Set
End Property
' Constructors
Public Sub New()
m_Roles = New List(Of String)
End Sub
Public Sub New(ByVal UserId As String, ByVal SecurityLevel As Integer)
m_UserId = UserId
m_SecurityLevel = SecurityLevel
End Sub
Public Sub New(ByVal UserId As String, ByVal Roles As List(Of String))
m_UserId = UserId
m_Roles = Roles
End Sub
Public Sub New(ByVal UserId As String, ByVal FirstName As String, _
ByVal LastName As String, ByVal SecurityLevel As Integer, _
ByVal Roles As List(Of String))
m_UserId = UserId
m_FirstName = FirstName
m_LastName = LastName
m_SecurityLevel = SecurityLevel
m_Roles = Roles
End Sub
' Public methods
Public Sub AddUserRole(ByVal RoleName As String)
If Not m_Roles.Contains(RoleName) Then
m_Roles.Add(RoleName)
End If
End Sub
Public Sub RemoveUserRole(ByVal RoleName As String)
If m_Roles.Contains(RoleName) Then
m_Roles.Remove(RoleName)
End If
End Sub
End Class
All of this is pretty straightforward. You can easily work with a UserInfo object to modify details and add it into a List collection without a problem.
But what happens if you need to find a given user within the list, or sort the list after adding several users to it? In the past you’ve had to essentially write your own collection type object, but thanks to existing interfaces and generics, we simply have to implement two features to make this possible.
IComparer(Of T)
The IComparer interface provides an interface for comparing one object to another. With the advent of generics, we can take the comparer one step further and specify the type of item we are comparing to, which eliminates the need to additional type casting within the method itself.
With out existing UserInfo object, simply add the following line to the class declaration:
Public Class UserInfo
Implements IComparer(Of UserInfo)
Then within our method, we need to implement the proper Comparer signature that the interface is looking for:
Public Function Compare(ByVal x As UserInfo, ByVal y As UserInfo) _
As Integer _
Implements System.Collections.Generic.IComparer(Of UserInfo).Compare
Return String.Compare(x.UserId, y.UserId)
End Function
In our case, we are going to use the UserId as the item to sort with since we know the usernames are going o be unique. The String.Compare() method returns the proper integer comparison (-1 for less than, 0 for equal, 1 for greater than) that the compare method uses when sorting a collection. You can easily wire this method up however you’d like based on what you want to compare. You can even write up multiple comparison type operators, and then feed a given comparison into the sort method of a collection to sort according to your needs.
Predicate Methods
In order to find a given user within the collection, we leverage a feature called the Predicate to make this happen. Again, in the old way of doing things you’d have to iterate through your custom object and manually look for the item you wanted. While the predicates are essentially doing the same thing, you can define them in ways that will allow you to return more than a single item. So you can do things like find all items in the collection where the last name is like “son” or something to that effect. I’m no expert on predicates, I’m just learning to use them myself, but there is a nice MSDN article on them here.
In our case, we want two simple predicates. One to find a given UserInfo object, and one to find a given UserInfo object based on the user Id (since that is our “primary key”) without having to create the entire object. Defining these methods looks like this:
Public Shared Function FindPredicate(ByVal User As UserInfo) _
As Predicate(Of UserInfo)
Return Function(User2 As UserInfo) User.UserId = User2.UserId
End Function
Public Shared Function FindPredicateByUserId(ByVal User As String) _
As Predicate(Of UserInfo)
Return Function(User2 As UserInfo) User = User2.UserId
End Function
That’s all there is to it. When you’re ready to use this object with a collection, you can do simple stuff like this:
Dim UserList As New List(Of UserInfo)
Dim LoadUser As UserInfo
Dim LostUser As UserInfo
Dim FoundUser AS UserInfo
LoadUser = New UserInfo("Sammy", 25)
UserList.Add(LoadUser)
LoadUser = New UserInfo("Hobart", 50)
UserList.Add(LoadUser)
LoadUser = New UserInfo("Fester", 10)
UserList.Add(LoadUser)
LoadUser = New UserInfo("Kozaky", 25)
UserList.Add(LoadUser)
UserList.Sort()
LostUser = New UserInfo("Hobart", 50)
FoundUser = UserList.Find(LostUser)
FoundUser = UserList.Find(UserInfo.FindPredicateByUserId _
(User.Identity.Name))
There you have it! Simple and plain and effective. I’m working on redoing the class so you can see it looks like when the interface and predicate functions are added. Stay tuned.
Great article
Thanks for providing a great overview of these list functions. It is hard to find this information in one spot and easy to follow.
You’re quite welcome! That’s part of the reason I keep this blog going, to save me from having to dig around in 20 places the next time I have to do the same thing. 8^D
A nice piece of codes. The download of your sample “UserInfo.zip (1.47 kb)” is broken. Kindly send me another link or copy of that file
Unfortunately I lost this file during my blog migration and system reformat, but I’ll try to throw something together for you. However, you should be able to piece together the details above into a class file.
Nice and easy, thanks!
thank you, very usefull..
the link to the source UserInfo.zip is broken..
little notice: the code need a “CompareTo” Function to work with the “Implements IComparable” declaration.. like this:
Implements IComparable
Public Overloads Function CompareTo(ByVal obj As Object) As Integer _
Implements IComparable.CompareTo
If obj Is Nothing Then Return 1
Dim otherUserInfo As UserInfo = TryCast(obj, UserInfo)
If otherUserInfo IsNot Nothing Then
Return Me.UserId.CompareTo(otherUserInfo.UserId)
Else
Throw New ArgumentException(“Object is not a UserInfo”)
End If
End Function
bye
Thanks David! That part was in the original zip file, which got lost during a blog migration. I’ll work on getting that together again, both in a C# and VB version. Thanks for pointing that out!