Skip navigation

Tag Archives: Generics

If you’re like me, then you like to experiment with new and different ways to do the usual programming tasks that you do. Perhaps its just a reaction to the boredom of always doing things the same way. Sometimes its an attempt to make the easy but time consuming tasks faster. Often is in a search for better looking, more abstract code. I like to think of it as a learning opportunity. The other day I was playing with sorting a list of objects. There are build in ways to do this in the List(Of T) class, so I though I would experiment with the different overloads of the List(Of T).Sort() method. While what is provided by the .NET Framework is adequate to make clean code, I played around anyways because you never know when you will run into something that doesn’t give you a clean solution out of the box. So just in case, its good to add more tools to your mental utility belt, at least that’s what I tell myself so it doesn’t feel like I’m wasting time.

For sorting an object based on an arbitrary property, the Sort() is not helpful as you cannot specify how the comparison is to be made. So that takes us to the first overload, Sort(IComparer(Of T)). This method is probably too cumbersome for standard adhoc and one time sorts that you may have to do. It requires that you pass it an instance of a class that implements IComparer(Of T), and the Compare method of that class will define your comparisons. One of my favorite features in .NET is generic types, as it increases code reuse substantially. So, why not make a generic class that takes a lambda function in the constructor to use as the comparison?


Public Class GenericCompare(Of T)
    Implements System.Collections.Generic.IComparer(Of T)

    Dim Func As Func(Of T, T, Integer)

    Public Sub New(ByVal Func As Func(Of T, T, Integer))
        Me.Func = Func
    End Sub

    Public Function Compare(ByVal x As T, ByVal y As T) As Integer Implements System.Collections.Generic.IComparer(Of T).Compare
        Return Func(x, y)
    End Function

End Class

Assuming a general object to sort like this one:


Public Class SomeObject

    Public Property Name As String

    Public Property Value1 As String

    Public Property Value2 As String

End Class

That will make our sort look like this:


        'Sort with custom compare
        Items.Sort(New GenericCompare(Of SomeObject)(Function(x, y)
                                                         If x.Name > y.Name Then
                                                             Return 1
                                                         ElseIf x.Name < y.Name Then
                                                             Return -1
                                                         Else
                                                             Return 0
                                                         End If
                                                     End Function))

The above uses a long implementation for the comparison, which could be used for customizing the sort order or sorting on multiple fields if needed. If you just want a basic string comparison of one property, you can shorten the above to this:


        'Or, sort with default string compare, one liner
        Items.Sort(New GenericCompare(Of SomeObject)(Function(x, y) String.Compare(x.Name, y.Name)))

Going crazy with this concept, you could even introduce an extension method to add another overload to Sort().


Public Module ExtensionMethods

    <System.Runtime.CompilerServices.Extension()>
    Public Sub Sort(Of T)(ByVal List As List(Of T), ByVal Func As Func(Of T, T, Integer))
        List.Sort(New GenericCompare(Of T)(Func))
    End Sub

End Module

Then your sort becomes this:


        'Or with the extension method 
        Items.Sort(Function(x As SomeObject, y As SomeObject) String.Compare(x.Name, y.Name))

I’m not sure why exactly, but if you notice in this usage, I had to specify the type for the x, y arguments. Unlike the last example, the compiler isn’t able to infer the type of the arguments.

Now, if you are looking at the different overloads for the Sort method, you may have notice that there is another overload that does basically what we just created. List(Of T).Sort(Comparison(Of T) takes a delegate to a function. So in this usage you would just have to create a function in your code that follows the Comparison(Of T) delegate. As we know however, a lambda expression can be used anywhere a delegate is expected, so we can shorten this up into a one-liner as well.


        Items.Sort(New Comparison(Of SomeObject)(Function(x As SomeObject, y As SomeObject) String.Compare(x.Name, y.Name)))

Although, the New Comparison(Of SomeObject)(...) is actually not necessary, and the use of this override can be shortened to the equivalent of the extension method example above.


Items.Sort(Function(x As SomeObject, y As SomeObject) String.Compare(x.Name, y.Name))

OK, so I played around a lot with the List(Of T).Sort methods and concluded that sorting a list of objects on an arbitrary property is pretty easy, and that .NET provides an overload that accepts a delegate or lambda for the comparison. So did I waste my time inventing that first method? Possibly, but I did prove to myself that you can create a generic class that can be made to accept lambdas that define its behavior. I can safely say I learned something and added another technique to the old toolbox, so in my onion a great use of time.

There are many things to consider when you write code. Often times you are focused on what you are trying to deliver to your customer. Lately, I’ve been trying to focus more on what will make my job as a programmer easier. Mostly that boils down to two things I like in my code, re-usability¬†and ease of updating. In the past I’ve found myself writing the same code over and over again, and when it came time for changes, updating the same code in a bunch of different places.

Consider this, you have a reasonably large number of forms/controls in your application and many of them contain a drop-down list of the same set of values. Usually, you might just edit each combo box and type in the values for each instance of the list on each form/control it appears on. However, if you need to update those values, you now have to do it in many places and you may forget one and thus introduce a bug into your program. One solution is to create a drop-down list that automatically loads its values from an enumeration. That way every instance of this list will always be the same and can be added by just dropping it onto the form/control without having to set its properties.

To do this, you need to first create a new class that inherits from ComboBox and accepts a type parameter T. This will be your generic base class that will do all the work.

Public Class EnumComboBox(Of T)
    Inherits ComboBox
End Class

Since you are inheriting from ComboBox, your new control has most of the functionality it needs already. Next, it’s just a matter of using reflection over the enumeration type passed in the type parameter T to fill in the DataSource property. You can do this in the constructor, and it looks like this:

    Public Sub New()
        MyBase.New()
        'Drop down list style, no text entry
        Me.DropDownStyle = ComboBoxStyle.DropDownList
        'Get type variable
        Dim EnumType As Type = GetType(T)
        'Check if it really is a enum
        If Not EnumType.IsEnum Then
            Throw New Exception(String.Format("Type {0} is not an enumeration.", EnumType.Name))
        End If
        'Get enum values
        Dim Values() As T = [Enum].GetValues(EnumType)
        'Setup datasource
        'Use linq query 
        Dim NewItems = From x In Values Select Key = [Enum].GetName(EnumType, x), Value = x
        'Set datasource to NewItems
        DataSource = NewItems.ToList
        'set display and value members
        DisplayMember = "Key"
        ValueMember = "Value"
    End Sub

First we are setting the DropDownStyle property to DropDownList, as this disables the free-entry text box portion of the ComboBox. The next statement, Dim EnumType As Type = GetType(T), retrieve a type variable that contains the reflection information we need. We also added a check to the IsEnum property just to make sure that the type parameter is actually and enumeration type. The line, Dim Values() As T = [Enum].GetValues(EnumType), is pretty simple and just retrieves an array of all the values in the enumeration. Next, we are using a LINQ statement to pair up each value with its name into an IEnumerable of a generic type. Note that we gave the properties in the generic type the specific names Key and Value. Then we set the Datasource property to be NewItems.ToList. Setting the Datasource directly to the LINQ query object doesn’t work, as the ComboBox class doesn’t seem to recognize query objects as a valid data source. ToList processes the query and converts it into a list object. Lastly, we just set the DisplayMember and ValueMember properties to “Key” and “Value”.

There are two other additions to this control. One is simply a property that exposes SelectedValue as type T rather that just plain object. This mostly just helps when coding against the control.

    Public ReadOnly Property EnumValue As T
        Get
            Return SelectedValue
        End Get
    End Property

The other addtion is just a hack to work around some problems with the forms designer. Basically what happens is the designer tries to serialize properties from the controls when you place them on a form/control. Well, if you try to serialize our datasource property, it will fail since it can’t serialize an anonymous type. That error will prevent you from adding your control to a form/control.

    <System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)>
    Shadows Property DataSource
        Get
            Return MyBase.DataSource
        End Get
        Set(ByVal value)
            MyBase.DataSource = value
        End Set
    End Property

That leaves us with the complete control code, shown here.

Public Class EnumComboBox(Of T)
    Inherits ComboBox

    Public Sub New()
        MyBase.New()
        'Drop down list style, no text entry
        Me.DropDownStyle = ComboBoxStyle.DropDownList
        'Get type variable
        Dim EnumType As Type = GetType(T)
        'Check if it really is a enum
        If Not EnumType.IsEnum Then
            Throw New Exception(String.Format("Type {0} is not an enumeration.", EnumType.Name))
        End If
        'Get enum values
        Dim Values() As T = [Enum].GetValues(EnumType)
        'Setup datasource
        'Use linq query 
        Dim NewItems = From x In Values Select Key = [Enum].GetName(EnumType, x), Value = x
        'Set datasource to NewItems
        DataSource = NewItems.ToList
        'set display and value members
        DisplayMember = "Key"
        ValueMember = "Value"
    End Sub

    <System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)>
    Shadows Property DataSource
        Get
            Return MyBase.DataSource
        End Get
        Set(ByVal value)
            MyBase.DataSource = value
        End Set
    End Property

    Public ReadOnly Property EnumValue As T
        Get
            Return SelectedValue
        End Get
    End Property
End Class

Now, another issue with the Visual Studio form designer, is that you cannot add a generically defined control through the control toolbox. To get it to work in the designer you just have to create a non-generic class, like this.

Public Class ComboBoxExample
    Inherits EnumComboBox(Of ExampleEnum)

End Class

At this point, if you build your project, ComboBoxExample should appear in your toolbox to be dropped onto your control.

That’s it. You now have a reusable drop down list, based on an enumeration that, no matter where it’s used will always reflect the correct values.