Reusing Generics Code
At this point, think about the things that your application normally needs to do during the save process that are the same for both tasks and persons. For example, before executing the save you probably want to perform all required validation, display a "saving" message, or other pre-processing. Then the code needs to call the business object to perform the unique processing, such as calling the appropriate
Save procedure. Finally, you may have some post-processing in the form, such as displaying a "save complete" message or disabling the Save button until something else is changed.
You may be writing this code now in every form. By writing a generic method, you can write such code one time and use it with every form and with any business object.
To easily use the code in every form, create a base form class. If you haven't spent a lot of time with form classes, see my article, "Give Your Forms a Base" in the March/April 2004 issue of CoDe Magazine.
Ensure every form then inherits from this base form class instead of System.Forms.Form by changing the code in the
.Designer.vb file as follows:
Partial Class GenericMethodExample
Inherits BaseWin
Then add the generic save method to the base form class:
Protected Sub Save(Of T)(ByVal objectToSave As T)
' Do any pre-processing
' Call the appropriate save method on the
' business object
Dim _methodInfo As Reflection.MethodInfo
_methodInfo = GetType(T).GetMethod("Save")
_methodInfo.Invoke(objectToSave, Nothing)
' Do any post processing
End Sub
The first thing that you will notice is that the generic method is declared slightly differently. The
(Of T) syntax indicates that the
Save method is a generic method. All generic methods require this syntax. You can define any number of type parameters, such as
(Of TSource, TDestination). You can then use whatever type parameter variable(s) you use in the method declaration throughout the method to represent that type.
Author's Note: Though most standards suggest using T or T with a suffix, such as TKey or TValue, you can actually use any letters for the type parameter placeholder. |
The method parameter in this example (
objectToSave) is also declared to be
As T, indicating that an object of the desired type must be passed to this method.
The first lines of code in this method would be your code for any processing required before actually saving the data. For example, this code could call validation methods or display a status message.
This method then uses reflection to call the appropriate
Save method on the business object, using the
GetMethod call to find the method on the defined business object and then invokes that method.
Any post-processing code comes last, such as displaying a "save complete" status message or disabling the Save button until the user makes another change.
Calling the method is easy. In the
Click event for the Save button on each form, add code similar to the following:
For Each individual As Person In PersonList
MyBase.Save(individual)
Next
This example calls the
Save method in the base form class for each person in the List.
The code for tasks is similar:
For Each item As Task In TaskList
MyBase.Save(item)
Next
By building generic methods, you can write generalized code that is type safe and works with any business object in your application.
Now that you know how to build a generic method, let's look at the comparer, which not only requires a generic method, but a generic class as well. Here's some code for a simple comparer class:
Public Class PropertyComparer(Of T)
Implements IComparer(Of T)
Private ReadOnly _propertyInfo As Reflection.PropertyInfo
Private ReadOnly _sortDirection As SortOrder
Public Sub New(ByVal propertyToSort As String, _
ByVal sortDirection As SortOrder)
_sortDirection = sortDirection
_propertyInfo = GetType(T).GetProperty(propertyToSort, _
Reflection.BindingFlags.Instance Or _
Reflection.BindingFlags.Public Or _
Reflection.BindingFlags.FlattenHierarchy Or _
Reflection.BindingFlags.IgnoreCase)
End Sub
Public Function Compare(ByVal x As T, ByVal y As T) _
As Integer
Implements IComparer(Of T).Compare
Dim xValue As Object = _propertyInfo.GetValue(x, Nothing)
Dim yValue As Object = _propertyInfo.GetValue(y, Nothing)
If _sortDirection = SortOrder.Ascending Then
Return System.Collections.Comparer.Default.Compare( _
xValue, yValue)
Else
Return System.Collections.Comparer.Default.Compare( _
yValue, xValue)
End If
End Function
End Class
This code defines a generic PropertyComparer class. It uses the
(Of T) syntax to denote that it is a generic class, and implements the IComparer generic interface to support comparing two values.
The class has two fields:
_propertyInfo and
_sortDirection. The
_propertyInfo field retains information on the property of the object selected for the sort. This allows you to sort on any business object property. The
_sortDirection field determines whether the sort should be ascending or descending.
The constructor takes the property name as a string and the sort direction and uses reflection to get information on the property based on the property name.
The generic
Compare method is called automatically during a sort operation to compare the values and sort them appropriately. This
Compare method calls the default comparer for the defined property type. For example, it will call the String comparer for properties of type String.
After the PropertyComparer code is in place, performing the sort using the PropertyComparer class is easy. To sort the TaskList, for example, you use this code:
TaskList.Sort(New PropertyComparer(Of _
Task)("TaskName", SortOrder.Ascending))
The built-in
Sort method of the generic List class takes a comparer as a parameter. In this example, the
Sort method creates a new PropertyComparer for the Task business object. It then passes in the property name designating the Task business object property to use for the sortin this case the
TaskName property. It also defines the sort order. The TaskList is then sorted as defined.
Create your own PropertyComparer class whenever you want to sort on a particular business property or when the default sorting features don't provide all of the power that you need. By creating this class in your base form class or code library, you can easily reuse it for any business object.
Refactor your methods to generic methods when you find similar methods that differ only in the data type that they use. You will greatly minimize the amount of code that you need to write for common functionality.