You are on page 1of 51

Using generics

By Ludwig Stuyck (Ludwig.Stuyck@ctg.com)


Last update: 28 February 2007

G enerics are one of the most useful improvements in the


.NET 2.0 framework. In this article I start with
pointing out the disadvantages of using the classical
arrays and .NET collections, and then I show you how
generics provide a nice and elegant way of creating flexible
and performant strongly typed collections with sorting
capabilities. I also explain the use of predicates to implement
custom behavior, and how you can create your own generic
classes. Along the way constraints, generic methods,
properties and interfaces are explained. Then I demonstrate
how you can data bind a generic collection to a DataGridView
control and how to serialize/deserialize the collection. Finally I
use the power of LINQ to query a generic collection.

Using arrays
Arrays provide a way to create strongly typed collections, for example:

int[] numbers = new int[] { 1, 2, 3, 4, 5 };


string[] names = new string[] { "Ludwig", "Leen" };

However, they are very limited: they can’t grow (they are rather static), and you
don’t have any convenience methods (like sorting, iterating …). In some cases
arrays will be sufficient, but mostly you need more flexibility. And more
flexibility we get with the .NET collection classes, like ArrayList.

Creating a simple collection with ArrayList


You have probably already used an ArrayList to create a collection of objects.
It’s a very simple and fast method, and it does the job, right? Let’s say, for
example, you need to create a collection of numbers and calculate the sum.

Page 1 of 51
Ctg Technical Articles

Create a little console application, call it Generics1, and add following code in
the Main method:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace Generics1
{
class Program
{
static void Main(string[] args)
{
// Create collection of numbers
ArrayList numbers = new ArrayList();
numbers.Add(1);
numbers.Add(5);

// Calculate total sum


int totalSum = 0;
foreach (int number in numbers)
{
totalSum += number;
}

Console.WriteLine(totalSum);
}
}
}

It works perfectly, and the program will write the result (6) to the console.

But this is not very elegant code, and I’ll tell you why. First of all, an ArrayList
is not strongly typed. And if you look at the signature of the Add method, you’ll
see that it accepts a parameter of type object. So instead of adding integers to
the collection, you can add every kind of object, for example, a string:

numbers.Add("some string");

This is a perfectly legal code, but when you run it, an InvalidCastException
is thrown because in the loop it will try to add the string to a number. Of course,

Using generic– By Ludwig Stuyck Page 2 of 51


Ctg Technical Articles

we know that adding a string is not valid, but the compiler will allow it anyway:
there’s no compile-time checking possible. Also, there’s a lot of casting going on:
when you add a number to the collection, there’s an implicit upcast to an object;
and visa versa: in the loop the objects are unboxed again to integers. Boxing and
unboxing are very time-consuming operations and should be avoided, because
they degrade performance. This effect of boxing/unboxing can be quite
significant in scenarios where you must iterate large collections.

Creating a strongly typed collection


In version 1.1 of the .NET framework, we could make our own strongly typed
collections by creating classes that inherit from existing framework collection
classes (like ArrayList), or by creating classes that inherit from abstract classes
(like CollectionBase or DictionaryBase). For example, let’s create a
strongly typed collection for a business object Person. Create a new console
application, call it Generics2, and add a new class called Person:

public class Person


{
private string name = string.Empty;
public string Name
{
get { return name; }
set { name = value; }
}

private string location = string.Empty;


public string Location
{
get { return location; }
set { location = value; }
}

private int age = 0;


public int Age
{
get { return age; }
set { age = value; }
}

public Person(string name, string location, int age)


{
this.name = name;
this.location = location;
this.age = age;
}
}

Using generic– By Ludwig Stuyck Page 3 of 51


Ctg Technical Articles

Then add a new class called PersonCollection and create the strongly typed
collection:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

public class PersonCollection : CollectionBase


{
public Person this[int index]
{
get
{
return (Person)List[index];
}
set
{
List[index] = value;
}
}

public int Add(Person person)


{
return List.Add(person);
}

public void Insert(int index, Person person)


{
List.Insert(index, person);
}

public void Remove(Person person)


{
List.Remove(person);
}

public bool Contains(Person person)


{
return List.Contains(person);
}

// Add other type-safe methods here


}

Now we can use our strongly typed collection as follows:

using System;
using System.Collections.Generic;
using System.Text;

namespace Generics2
{

Using generic– By Ludwig Stuyck Page 4 of 51


Ctg Technical Articles

class Program
{
static void Main(string[] args)
{
// Create some persons
Person person1 =
new Person("Ludwig Stuyck", "Zaventem", 33);
Person person2 = new Person("Leen Rans", "Zaventem", 25);

// Create collection and add customers


PersonCollection persons = new PersonCollection();
persons.Add(person1);
persons.Add(person2);

// Write persons to console


foreach (Person person in persons)
{
Console.WriteLine("{0}, {1}, {2}",
person.Name, person.Location, person.Age);
}
}
}
}

As you see, we can only add objects of type Person to the person’s collection.
If we would add another type of object, the compiler would give an error. So we
have created a strongly typed collection that only accepts Person objects;
however, there’s still a lot of casting going on in our PersonCollection class.
As expected, the output is:

Generics
Time to introduce a new feature of .NET framework 2.0: generics. Generics in C#
support defining algorithms and defer the data type until the point of use.
They are a type of data structure that contains a code that remains the same;
however, the data type of the parameters can change with each use. So in
general, it’s a code template that can be applied to use the same code, using
various types. They are also called parameterized types or parametric
polymorphism. Generics have a number of important advantages:

Using generic– By Ludwig Stuyck Page 5 of 51


Ctg Technical Articles

Type safety: generic classes work with a well-determined object type.


Binary code reuse: generic code is generated at run-time, so different
usages of a generic type reuse most of the time the same JIT-code.
Performance: type checking is done at compile time, not at run-time; and
boxing/unboxing of values is unnecessary, so generics are a lot faster.
Clarity: constraints make it clear what type of object a generic type can
handle.

Let’s explain this further with an example.

Create a new console application and call it Generics3. Again, add the business
object Person that we defined earlier. Then, in the Main method, create a
collection of Person objects as follows:

using System;
using System.Collections.Generic;
using System.Text;

namespace Generics3
{
class Program
{
static void Main(string[] args)
{
// Create some persons
Person person1 =
new Person("Ludwig Stuyck", "Zaventem", 33);
Person person2 = new Person("Leen Rans", "Zaventem", 25);

// Create collection and add persons


List<Person> persons = new List<Person>();
persons.Add(person1);
persons.Add(person2);

// Write customers to console


foreach (Person person in persons)
{
Console.WriteLine("{0}, {1}, {2}",
person.Name, person.Location, person.Age);
}
}
}
}

And the result is:

Using generic– By Ludwig Stuyck Page 6 of 51


Ctg Technical Articles

As you see, we can define a strongly typed list in first line:

List<Person> persons = new List<Person>();

You can immediately see the huge advantage because we don’t have to write our
custom collection classes (like PersonCollection) anymore! Moreover, there’s
no need for casting objects.

We could in the same way define and use a collection that holds integers:

// Create collection and add numbers


List<int> numbers = new List<int>();
numbers.Add(1);
numbers.Add(5);

// Calculate sum
int totalSum = 0;
foreach (int number in numbers)
{
totalSum += number;
}

Console.WriteLine(totalSum);

Finding an object in a List<> collection


Suppose we want to find a specific person in the persons list. We can easily do so
thanks to predicates. A predicate represents the method that defines a set of
criteria and determines whether the specified object meets those criteria. Let’s see
how it works in the following example.

First, we create a PersonNameFilter class that implements a method


FilterByName. This class contains a name variable that contains the name of the
customer that we want to find.
public class PersonNameFilter
{

Using generic– By Ludwig Stuyck Page 7 of 51


Ctg Technical Articles

private string name = string.Empty;

public PersonNameFilter(string name)


{
this.name = name;
}

public bool FilterByName(Person person)


{
return person.Name.Equals(name);
}
}

This class is then used to look for a specific person:

// Create some persons


Person person1 = new Person("Ludwig Stuyck", "Zaventem", 33);
Person person2 = new Person("Leen Rans", "Zaventem", 25);

// Create collection and add persons


List<Person> persons = new List<Person>();
persons.Add(person1);
persons.Add(person2);

// Create a new person name filter, that will look


// for the specified name
PersonNameFilter personNameFilter =
new PersonNameFilter("Ludwig Stuyck");
// Create a new predicate, that uses the FilterByName method
// to determine whether the person has been found
Predicate<Person> filterByName = new
Predicate<Person>(personNameFilter.FilterByName);
// Find the person in the collection
Person foundPerson = persons.Find(filterByName);

When the Find method is called, the FilterByName method is executed for
each person in the collection, until the expression in the method returns true.
When the method returns true, the current Person object will be returned by the
Find method. When the method never returns true (so the person that is
searched for is not in the collection), then the Find method will return null.

Finding an object collection in a List<>


collection
Suppose we need to look for persons that live in Zaventem. We create a new
class PersonLocationFilter, which contains a method
FilterByLocation:

public class PersonLocationFilter

Using generic– By Ludwig Stuyck Page 8 of 51


Ctg Technical Articles

{
private string location = string.Empty;

public PersonLocationFilter(string location)


{
this.location = location;
}

public bool FilterByLocation(Person person)


{
return person.Location.Equals(location);
}
}

And again we can use the Find method to look for the persons that lives in
Zaventem:

// Create a new person location filter, that will look for the specified
location
PersonLocationFilter personLocationFilter = new
PersonLocationFilter("Zaventem");
// Create a new predicate, that uses the FilterByLocation method
// to determine whether the person has been found
Predicate<Person> filterByLocation = new
Predicate<Person>(personLocationFilter.FilterByLocation);
// Find the person in the collection
Person foundPerson = persons.Find(filterByLocation);

However, there is more than one person living in Zaventem, and we want to find
them all. We can do so by using the FindAll method:

List<Person> foundPersons = persons.FindAll(filterByLocation);

This method will return a collection of Person objects, instead of a single


Person object. When the FindAll method is executed, the
FilterByLocation method is executed for each person in the collection. Every
time the FilterByLocation method returns true, the current Person object is
added to the resulting collection.

Iterating a list to perform operations


Suppose you want to find the oldest person in the collection. You could do it like
this:

int oldestAge = 0;
foreach (Person person in persons)
{
if (person.Age > oldestAge)
oldestAge = person.Age;

Using generic– By Ludwig Stuyck Page 9 of 51


Ctg Technical Articles

}
Console.WriteLine("{0}", oldestAge);

However, there’s a new, more object oriented way of doing this. First, create a
class PersonAgeCalculator that is responsible for the calculation, as follows:

public class PersonAgeCalculator


{
private int oldestAge = 0;
public int OldestAge
{
get { return oldestAge; }
set { oldestAge = value; }
}

public void CalculateOldestAge(Person person)


{
if (person.Age > oldestAge)
oldestAge = person.Age;
}
}

And then, create an Action object and pass it on to the ForEach method of the
person collection:

// Create a new person age calculator


PersonAgeCalculator personAgeCalculator = new PersonAgeCalculator();
// Create a new action, that corresponds to the
// CalculateOldestAge method
Action<Person> action = new
Action<Person>(personAgeCalculator.CalculateOldestAge);
// Execute the action for each person in the collection
persons.ForEach(action);
Console.WriteLine("{0}", personAgeCalculator.OldestAge);

Sorting generic lists


Sorting a generic list can be done by calling the Sort method on the list.
However, to make this work, the objects in the list must know how to compare
themselves. In our example, the Person objects need to know how to compare
each other. We can do so by implementing the IComparable interface, meaning
we have to implement the CompareTo method:

public class Person : IComparable


{
private string name = null;
public string Name
{

Using generic– By Ludwig Stuyck Page 10 of 51


Ctg Technical Articles

get { return name; }


set { name = value; }
}

private string location = null;


public string Location
{
get { return location; }
set { location = value; }
}

private int age = 0;


public int Age
{
get { return age; }
set { age = value; }
}

public Person(string name, string location, int age)


{
this.name = name;
this.location = location;
this.age = age;
}

public int CompareTo(object obj)


{
Person person = (Person)obj;
return this.name.CompareTo(person.Name);
}
}

As you see, the Person class knows now that it has to compare the Name property
if two objects are compared. Now we can use the Sort method:

static void Main(string[] args)


{
// Create some persons
Person person1 = new Person("Leen", "Steenokkerzeel", 32);
Person person2 = new Person("Ludwig", "Steenokkerzeel", 29);
Person person3 = new Person("Paulien", "Zaventem", 2);

// Create collection and add persons


List<Person> persons = new List<Person>();
persons.Add(person1);
persons.Add(person2);
persons.Add(person3);

// Write persons to console


foreach (Person person in persons)
{
Console.WriteLine("{0}, {1}, {2}",
person.Name, person.Location, person.Age);

Using generic– By Ludwig Stuyck Page 11 of 51


Ctg Technical Articles

Console.WriteLine();

// Sort collection
persons.Sort();

// Write sorted persons to console


foreach (Person person in persons)
{
Console.WriteLine("{0}, {1}, {2}",
person.Name, person.Location, person.Age);
}
}

The output is:

So sorting works. There is still room for some improvement here. Notice that
there was still casting needed in the CompareTo method, when we implemented
the IComparable interface. Indeed, the IComparable interface forces us to
implement the CompareTo method, which takes an object as a parameter:

public int CompareTo(object obj)


{
// Cast object back to a Person first
Person person = (Person)obj;
return this.name.CompareTo(person.Name);
}

As we have said before, we should avoid casting, and that’s what we’ll do next.
The solution to this problem is generic interfaces. Instead of implementing the
IComparable interface, we will implement its generic counter part, the
IComparable<Person> interface:

public class Person : IComparable<Person>


{
private string name = null;

Using generic– By Ludwig Stuyck Page 12 of 51


Ctg Technical Articles

public string Name


{
get { return name; }
set { name = value; }
}

private string location = null;


public string Location
{
get { return location; }
set { location = value; }
}

private int age = 0;


public int Age
{
get { return age; }
set { age = value; }
}

public Person(string name, string location, int age)


{
this.name = name;
this.location = location;
this.age = age;
}

public int CompareTo(Person person)


{
return this.name.CompareTo(person.Name);
}
}

As a consequence, the CompareTo method does not have an object parameter


anymore, but a parameter of type Person, avoiding any casting.

Extending sorting
In the previous example we implemented sorting. But, we did not have the
possibility to specify on which property the sort should be done. It would be nice
if we could sort the collection by another property. First of all, we will create our
own custom implementation of IComparer. We’ll add a new class
PersonComparer, that implements the generic interface IComparer<Person>.
This class knows about Person objects, and knows how to sort them. But before
we do that, create an enumeration with supported comparison types:

// Enumeration of comparison types


public enum PersonComparisonType
{

Using generic– By Ludwig Stuyck Page 13 of 51


Ctg Technical Articles

Name,
Location,
Age
};

And now, implement the PersonComparer class:

public class PersonComparer : IComparer<Person>


{
private PersonComparisonType comparisonType =
PersonComparisonType.Name;
public PersonComparisonType ComparisonType
{
get { return comparisonType; }
set { comparisonType = value; }
}

public PersonComparer(PersonComparisonType comparisonType)


{
this.comparisonType = comparisonType;
}

public bool Equals(Person lhs, Person rhs)


{
return this.Compare(lhs, rhs) == 0;
}

// Tell the Person objects to compare themselves


public int Compare(Person lhs, Person rhs)
{
return lhs.CompareTo(rhs, comparisonType);
}
}

The idea is to pass an instance of this class to the Sort method of the List. In
the class we implemented the method Compare, and here we use a new method
CompareTo of the Person objects to do the comparison, passing the
ComparisonType. So we will add this method to the Person class.

public class Person : IComparable<Person>


{
private string name = null;
public string Name
{
get { return name; }
set { name = value; }
}

private string location = null;


public string Location
{
get { return location; }

Using generic– By Ludwig Stuyck Page 14 of 51


Ctg Technical Articles

set { location = value; }


}

private int age = 0;


public int Age
{
get { return age; }
set { age = value; }
}

public Person(string name, string location, int age)


{
this.name = name;
this.location = location;
this.age = age;
}

public int CompareTo(Person person)


{
return this.name.CompareTo(person.Name);
}

// Special implementation to be called by custom comparer


public int CompareTo(Person person,
PersonComparisonType comparisonType)
{
switch (comparisonType)
{
case PersonComparisonType.Name:
return this.name.CompareTo(person.Name);
case PersonComparisonType.Age:
return this.age.CompareTo(person.Age);
case PersonComparisonType.Location:
return this.location.CompareTo(person.Location);
}
return 0;
}
}

Depending on the passed comparison type, the correct properties are compared
to each other. Now we can use the PersonComparer class to sort the collection,
for example using age:

static void Main(string[] args)


{
// Create some persons
Person person1 = new Person("Leen", "Steenokkerzeel", 32);
Person person2 = new Person("Ludwig", "Steenokkerzeel", 29);
Person person3 = new Person("Jonathan", "Zaventem", 7);

// Create collection and add persons


List<Person> persons = new List<Person>();
persons.Add(person1);

Using generic– By Ludwig Stuyck Page 15 of 51


Ctg Technical Articles

persons.Add(person2);
persons.Add(person3);

// Write persons to console


foreach (Person person in persons)
{
Console.WriteLine("{0}, {1}, {2}",
person.Name, person.Location, person.Age);
}

Console.WriteLine();

// Sort collection using age


PersonComparer personComparer =
new PersonComparer(PersonComparisonType.Age);
persons.Sort(personComparer);

// Write sorted persons to console


foreach (Person person in persons)
{
Console.WriteLine("{0}, {1}, {2}",
person.Name, person.Location, person.Age);
}
}

And the output is:

Extending sorting once more


In the previous example we could already set the property we wanted to sort on,
but the items are always sorted in an ascending way. Maybe you want this to be
descending… Let’s implement this. First, create a SortOrderType enumeration:

// Enumeration of sorder order types


public enum SortOrderType
{
Ascending,
Descending
};

Using generic– By Ludwig Stuyck Page 16 of 51


Ctg Technical Articles

Add a SortOrder property to the PersonComparer class, and use this


property in the Compare method to determine the comparison method:

public class PersonComparer : IComparer<Person>


{
private PersonComparisonType comparisonType =
PersonComparisonType.Name;
public PersonComparisonType ComparisonType
{
get { return comparisonType; }
set { comparisonType = value; }
}

private SortOrderType sortOrder = SortOrderType.Ascending;


public SortOrderType SortOrder
{
get { return sortOrder; }
set { sortOrder = value; }
}

public PersonComparer(PersonComparisonType comparisonType)


{
this.comparisonType = comparisonType;
}

public bool Equals(Person lhs, Person rhs)


{
return this.Compare(lhs, rhs) == 0;
}

// Tell the Person objects to compare themselves


public int Compare(Person lhs, Person rhs)
{
switch (sortOrder)
{
case SortOrderType.Ascending:
default:
return lhs.CompareTo(rhs, comparisonType);
case SortOrderType.Descending:
return rhs.CompareTo(lhs, comparisonType);
}
}
}

Now you can use the SortOrder property to set the sort order:

static void Main(string[] args)


{
// Create some persons
Person person1 = new Person("Leen", "Steenokkerzeel", 32);
Person person2 = new Person("Ludwig", "Steenokkerzeel", 29);

Using generic– By Ludwig Stuyck Page 17 of 51


Ctg Technical Articles

Person person3 = new Person("Jonathan", "Zaventem", 7);

// Create collection and add persons


List<Person> persons = new List<Person>();
persons.Add(person1);
persons.Add(person2);
persons.Add(person3);

// Write persons to console


foreach (Person person in persons)
{
Console.WriteLine("{0}, {1}, {2}",
person.Name, person.Location, person.Age);
}

Console.WriteLine();

// Sort collection using age


PersonComparer personComparer = new
PersonComparer(PersonComparisonType.Age);
personComparer.SortOrder = SortOrderType.Descending;
persons.Sort(personComparer);

// Write sorted persons to console


foreach (Person person in persons)
{
Console.WriteLine("{0}, {1}, {2}",
person.Name, person.Location, person.Age);
}
}

Generic classes
You can also create your own generic classes. Generic classes encapsulate
operations that are not specific to any particular data type. As an example, let’s
create our own generic class Tree. This tree will represent an in-memory
hierarchical representation of objects of type T. Create a new console application
and call it Generics4. Add the Person class again and then define a generic
class Tree as follows:

public class Tree<T>


{
}

If we want to use this tree (of course, the Tree class does not do anything yet) in
our code, we could create an instance as follows:

static void Main(string[] args)


{

Using generic– By Ludwig Stuyck Page 18 of 51


Ctg Technical Articles

// Create the generic tree that will work with Person objects
Tree<Person> personHierarchy = new Tree<Person>();
}

Let’s implement the tree now. A tree consists out of tree nodes that can work
with this type T, so we’ll need a TreeNode class, which is also a generic class:

public class TreeNode<T>


{
}

The top of the tree hierarchy is a root node, so we’ll add a RootNode property to
the Tree class, so this is a generic property:

public class Tree<T>


{
private TreeNode<T> rootNode = default(TreeNode<T>);
public TreeNode<T> RootNode
{
get { return rootNode; }
set { rootNode = value; }
}
}

Note the default () operator, which returns the default value of a type. It can be
used to initialize an object to its default value.

We can now set the root property as follows:

static void Main(string[] args)


{
// Create the generic tree that will work with Person objects
Tree<Person> personHierarchy = new Tree<Person>();

// Create a tree node object, that will work with Person objects
TreeNode<Person> grandFatherNode = new TreeNode<Person>();

// Set root property of tree to the grandFather tree node


personHierarchy.RootNode = grandFatherNode;
}

Each tree node we create, has to have a reference to a particular object of type T.
Therefore, we will add a Tag property to the TreeNode class, this also is a
generic property:

public class TreeNode<T>


{
private T tag = default(T);

Using generic– By Ludwig Stuyck Page 19 of 51


Ctg Technical Articles

public T Tag
{
get { return tag; }
set { tag = value; }
}
}

So the Tag property of a tree node points to an object of type T.

Now, if we create such a tree node, it would be handy if we could immediately


pass its assigned object of type T. So we’ll add a constructor to the TreeNode
class with one parameter:

public class TreeNode<T>


{
private T tag = default(T);
public T Tag
{
get { return tag; }
set { tag = value; }
}

public TreeNode(T tag)


{
this.tag = tag;
}
}

This forces us to assign an object of type T to a tree node each time we create a
tree node. So we need to modify our code a little bit to be able to pass a Person
object when we create a tree node:

static void Main(string[] args)


{
// Create the generic tree that will work with Person objects
Tree<Person> personHierarchy = new Tree<Person>();

// Create a person object


Person grandFather = new Person("Louis", "Zaventem", 92);

// Create a tree node that is assigned to the person object


TreeNode<Person> grandFatherNode
= new TreeNode<Person>(grandFather);

// Set root property of tree to the grandFather tree node


personHierarchy.RootNode = grandFatherNode;
}

Using generic– By Ludwig Stuyck Page 20 of 51


Ctg Technical Articles

Right, this works… Now we will rewrite the Tree class a bit, so that it creates
tree nodes for us. Therefore we will add a method CreateNode to the Tree
class that will create and return a tree node for us. This is a generic method:

public class Tree<T>


{
private TreeNode<T> rootNode = default(TreeNode<T>);
public TreeNode<T> RootNode
{
get { return rootNode; }
set { rootNode = value; }
}

public TreeNode<T> CreateNode(T tag)


{
return new TreeNode<T>(tag);
}
}

So now, instead of creating the tree node ourselves, we will use the new method
we have just created:

static void Main(string[] args)


{
// Create the generic tree that will work with Person objects
Tree<Person> personHierarchy = new Tree<Person>();

// Create a person object


Person grandFather = new Person("Louis", "Zaventem", 92);
// Let the tree create and return a tree node (assigned to the
// person object we just created, and assign this tree node to
// the root node
personHierarchy.RootNode = personHierarchy.CreateNode(grandFather);
}

There’s something missing: our tree has a root node, but a root node should have
child nodes. So add a property Nodes to the TreeNode class, and this should be a
list of tree nodes:

public class TreeNode<T>


{
private T tag = default(T);
public T Tag
{
get { return tag; }
set { tag = value; }
}

private List<TreeNode<T>> nodes = new List<TreeNode<T>>();


public List<TreeNode<T>> Nodes

Using generic– By Ludwig Stuyck Page 21 of 51


Ctg Technical Articles

{
get { return nodes; }
set { nodes = value; }
}

public TreeNode(T tag)


{
this.tag = tag;
}
}

Now we can add some more nodes to our tree:

static void Main(string[] args)


{
// Create the generic tree that will work with Person objects
Tree<Person> personHierarchy = new Tree<Person>();

// Create person objects


Person grandFather = new Person("Louis", "Zaventem", 92);
Person son1 = new Person("Michael", "Ukkel", 65);
Person son2 = new Person("Jan", "Leuven", 62);
Person daughter = new Person("Jennifer", "Zaventem", 57);
Person grandson = new Person("Kobe", "Steenokkerzeel", 26);
Person grandDaughter = new Person("Eowyn", "Steenokkerzeel", 23);

// Let the tree create and return a tree node (assigned to the
// person object we just created, and assign this tree node to
// the root node
personHierarchy.RootNode = personHierarchy.CreateNode(grandFather);

// Create child nodes and add them to the tree

// son1 is child of root node


TreeNode<Person> treeNodeSon1 = personHierarchy.CreateNode(son1);
personHierarchy.RootNode.Nodes.Add(treeNodeSon1);

// son2 is child of root node


TreeNode<Person> treeNodeSon2 = personHierarchy.CreateNode(son2);
personHierarchy.RootNode.Nodes.Add(treeNodeSon2);

// daughter is child of root node


TreeNode<Person> treeNodedaughter =
personHierarchy.CreateNode(daughter);
personHierarchy.RootNode.Nodes.Add(treeNodedaughter);

// grandson is child of daughter


TreeNode<Person> treeNodegrandson =
personHierarchy.CreateNode(grandson);
treeNodedaughter.Nodes.Add(treeNodegrandson);

// granddaughter is child of daughter


TreeNode<Person> treeNodegrandDaughter =
personHierarchy.CreateNode(grandDaughter);

Using generic– By Ludwig Stuyck Page 22 of 51


Ctg Technical Articles

treeNodedaughter.Nodes.Add(treeNodegrandDaughter);
}

We have now created a tree with a root node, this root node has three child
nodes, and one of the child nodes has two child nodes. Each node is assigned to
an object of type Person. So what we have created is this hierarchy:

Louis
+-- Michael
+-- Jan
+-- Jennifer
+-- Kobe
+-- Eowyn

Note that we can easily reuse the Tree to work with other types. For example,
we could create a tree that works with strings, Customer or any other type.
And that’s the whole idea of using generics: write once, use many.

Derivation constraints
We want to implement sorting capability in our tree. Sorting simply means that
we need to sort each child node collection in the tree. But before we do this, first
write some code to display the tree hierarchy to the console:

static void Main(string[] args)


{
// Create the generic tree that will work with Person objects
Tree<Person> personHierarchy = new Tree<Person>();

// Create person objects


Person grandFather = new Person("Louis", "Zaventem", 92);
Person son1 = new Person("Michael", "Ukkel", 65);
Person son2 = new Person("Jan", "Leuven", 62);
Person daughter = new Person("Jennifer", "Zaventem", 57);
Person grandson = new Person("Kobe", "Steenokkerzeel", 26);
Person grandDaughter = new Person("Eowyn", "Steenokkerzeel", 23);

// Let the tree create and return a tree node (assigned to the
// person object we just created, and assign this tree node to
// the root node
personHierarchy.RootNode = personHierarchy.CreateNode(grandFather);

// Create child nodes and add them to the tree

// son1 is child of root node


TreeNode<Person> treeNodeSon1 = personHierarchy.CreateNode(son1);
personHierarchy.RootNode.Nodes.Add(treeNodeSon1);

Using generic– By Ludwig Stuyck Page 23 of 51


Ctg Technical Articles

// son2 is child of root node


TreeNode<Person> treeNodeSon2 = personHierarchy.CreateNode(son2);
personHierarchy.RootNode.Nodes.Add(treeNodeSon2);

// daughter is child of root node


TreeNode<Person> treeNodedaughter =
personHierarchy.CreateNode(daughter);
personHierarchy.RootNode.Nodes.Add(treeNodedaughter);

// grandson is child of daughter


TreeNode<Person> treeNodegrandson =
personHierarchy.CreateNode(grandson);
treeNodedaughter.Nodes.Add(treeNodegrandson);

// granddaughter is child of daughter


TreeNode<Person> treeNodegrandDaughter =
personHierarchy.CreateNode(grandDaughter);
treeNodedaughter.Nodes.Add(treeNodegrandDaughter);

string spaces = string.Empty;


PrintNode(personHierarchy.RootNode, spaces);
}

public static void PrintNode(TreeNode<Person> node, string spaces)


{
Console.WriteLine(spaces + node.Tag.Name);
spaces += " ";
foreach (TreeNode<Person> childNode in node.Nodes)
{
PrintNode(childNode, spaces);
}
}

The result looks like:

Now we’re ready to implement sorting. It would be nice if we could just say to
the Tree object that it should sort its nodes. So we’ll add a Sort method to the
Tree class. This method will then call the Sort method of the root node, which
will cause a recursive sorting mechanism: the root node will sort its child nodes

Using generic– By Ludwig Stuyck Page 24 of 51


Ctg Technical Articles

collection and for each child node, it will call the Sort method again (recursive
method).

However, there’s a problem. A tree node does not now how to sort its collection
of tree nodes. Therefore we need to make sure that the TreeNode class
implements the IComparable interface, because when we do so, it forces us to
implement the CompareTo method in the TreeNode class. In this method, the
comparison method of the objects of type T will be called, which means that each
Tag property has to be compared. In our case, it means that each Person has to
be compared. The Person class knows how to compare itself, because we have
already implemented the IComparable interface previously.
The first step is to make sure that tree nodes know how they have to compare to
each other, by implementing the IComparable<TreeNode<T>> generic
interface:

public class TreeNode<T> : IComparable<TreeNode<T>>


{
private T tag = default(T);
public T Tag
{
get { return tag; }
set { tag = value; }
}

private List<TreeNode<T>> nodes = new List<TreeNode<T>>();


public List<TreeNode<T>> Nodes
{
get { return nodes; }
set { nodes = value; }
}

public TreeNode(T tag)


{
this.tag = tag;
}

public int CompareTo(TreeNode<T> treeNode)


{
return this.tag.CompareTo(treeNode.Tag);
}
}

As we have used the generic interface IComparable<TreeNode<T>>, we don’t


need any casting in the CompareTo method.

Next, as we’ve said, the tree should have a Sort method, that will call the root
code Sort method:

Using generic– By Ludwig Stuyck Page 25 of 51


Ctg Technical Articles

public class Tree<T>


{
private TreeNode<T> rootNode = default(TreeNode<T>);
public TreeNode<T> RootNode
{
get { return rootNode; }
set { rootNode = value; }
}

public TreeNode<T> CreateNode(T tag)


{
return new TreeNode<T>(tag);
}

public void Sort()


{
rootNode.Sort();
}
}

Evidently, the TreeNode class should also have a Sort method, which will first
sort its child nodes collection, and then call itself again for each child node:

public class TreeNode<T> : IComparable<TreeNode<T>>


{
private T tag = default(T);
public T Tag
{
get { return tag; }
set { tag = value; }
}

private List<TreeNode<T>> nodes = new List<TreeNode<T>>();


public List<TreeNode<T>> Nodes
{
get { return nodes; }
set { nodes = value; }
}

public TreeNode(T tag)


{
this.tag = tag;
}

public void Sort()


{
nodes.Sort();
foreach (TreeNode<T> node in nodes)
{
node.Sort();
}
}

Using generic– By Ludwig Stuyck Page 26 of 51


Ctg Technical Articles

public int CompareTo(TreeNode<T> treeNode)


{
return this.tag.CompareTo(treeNode.Tag);
}
}

This won’t compile yet, because we take for granted that the type T implements
the IComparable interface. Imagine we would use the tree with an object type
that cannot compare itself (it does not implement IComparable…) then we
would have major problems, because sorting would not work. So our tree should
enforce user to use a type T that implements the IComparable interface: we
have to add a constraint to the Tree class:

public class Tree<T> where T : IComparable<T>


{

}

By doing this, we say that the type T must implement the IComparable<T>
interface; and therefore are sure that this type T knows how to compare itself;
and sorting can work correctly.

Still one more thing to do: the type T defined in the Tree class is also used in the
TreeNode class. So we also need to make sure that the type T in the TreeNode
class implements the IComparable<T> interface:

public class TreeNode<T> : IComparable<TreeNode<T>>


where T : IComparable<T>
{

}

Let’s test our code:

static void Main(string[] args)


{
// Create the generic tree that will work with Person objects
Tree<Person> personHierarchy = new Tree<Person>();

// Create person objects


Person grandFather = new Person("Louis", "Zaventem", 92);
Person son1 = new Person("Michael", "Ukkel", 65);
Person son2 = new Person("Jan", "Leuven", 62);
Person daughter = new Person("Jennifer", "Zaventem", 57);
Person grandson = new Person("Kobe", "Steenokkerzeel", 26);
Person grandDaughter = new Person("Eowyn", "Steenokkerzeel", 23);

Using generic– By Ludwig Stuyck Page 27 of 51


Ctg Technical Articles

// Let the tree create and return a tree node (assigned to the
// person object we just created, and assign this tree node to
// the root node
personHierarchy.RootNode = personHierarchy.CreateNode(grandFather);

// Create child nodes and add them to the tree

// son1 is child of root node


TreeNode<Person> treeNodeSon1 = personHierarchy.CreateNode(son1);
personHierarchy.RootNode.Nodes.Add(treeNodeSon1);

// son2 is child of root node


TreeNode<Person> treeNodeSon2 = personHierarchy.CreateNode(son2);
personHierarchy.RootNode.Nodes.Add(treeNodeSon2);

// daughter is child of root node


TreeNode<Person> treeNodedaughter =
personHierarchy.CreateNode(daughter);
personHierarchy.RootNode.Nodes.Add(treeNodedaughter);

// grandson is child of daughter


TreeNode<Person> treeNodegrandson =
personHierarchy.CreateNode(grandson);
treeNodedaughter.Nodes.Add(treeNodegrandson);

// granddaughter is child of daughter


TreeNode<Person> treeNodegrandDaughter =
personHierarchy.CreateNode(grandDaughter);
treeNodedaughter.Nodes.Add(treeNodegrandDaughter);

// Write person hierarchy to console


string spaces = string.Empty;
PrintNode(personHierarchy.RootNode, spaces);

Console.WriteLine();

// Sort hierarchy
personHierarchy.Sort();

// Write sorted person hierarchy to console


spaces = string.Empty;
PrintNode(personHierarchy.RootNode, spaces);
}

And the output is:

Using generic– By Ludwig Stuyck Page 28 of 51


Ctg Technical Articles

To be complete, here’s the entire code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace Generics
{
class Program
{
static void Main(string[] args)
{
// Create the generic tree that will work
// with Person objects
Tree<Person> personHierarchy = new Tree<Person>();

// Create person objects


Person grandFather = new Person("Louis", "Zaventem", 92);
Person son1 = new Person("Michael", "Ukkel", 65);
Person son2 = new Person("Jan", "Leuven", 62);
Person daughter = new Person("Jennifer", "Zaventem", 57);
Person grandson = new Person("Kobe", "Steenokkerzeel", 26);
Person grandDaughter =
new Person("Eowyn", "Steenokkerzeel", 23);

// Let the tree create and return a tree node


// (assigned to the person object we just created,
// and assign this tree node to the root node
personHierarchy.RootNode =
personHierarchy.CreateNode(grandFather);

// Create child nodes and add them to the tree

// son1 is child of root node


TreeNode<Person> treeNodeSon1 =

Using generic– By Ludwig Stuyck Page 29 of 51


Ctg Technical Articles

personHierarchy.CreateNode(son1);
personHierarchy.RootNode.Nodes.Add(treeNodeSon1);

// son2 is child of root node


TreeNode<Person> treeNodeSon2 =
personHierarchy.CreateNode(son2);
personHierarchy.RootNode.Nodes.Add(treeNodeSon2);

// daughter is child of root node


TreeNode<Person> treeNodedaughter =
personHierarchy.CreateNode(daughter);
personHierarchy.RootNode.Nodes.Add(treeNodedaughter);

// grandson is child of daughter


TreeNode<Person> treeNodegrandson =
personHierarchy.CreateNode(grandson);
treeNodedaughter.Nodes.Add(treeNodegrandson);

// granddaughter is child of daughter


TreeNode<Person> treeNodegrandDaughter =
personHierarchy.CreateNode(grandDaughter);
treeNodedaughter.Nodes.Add(treeNodegrandDaughter);

// Write person hierarchy to console


string spaces = string.Empty;
PrintNode(personHierarchy.RootNode, spaces);

Console.WriteLine();

// Sort hierarchy
personHierarchy.Sort();

// Write sorted person hierarchy to console


spaces = string.Empty;
PrintNode(personHierarchy.RootNode, spaces);
}

public static void PrintNode(


TreeNode<Person> node, string spaces)
{
Console.WriteLine(spaces + node.Tag.Name);
spaces += " ";
foreach (TreeNode<Person> childNode in node.Nodes)
{
PrintNode(childNode, spaces);
}
}

public class Tree<T> where T : IComparable<T>


{
private TreeNode<T> rootNode = default(TreeNode<T>);
public TreeNode<T> RootNode
{
get { return rootNode; }

Using generic– By Ludwig Stuyck Page 30 of 51


Ctg Technical Articles

set { rootNode = value; }


}

public TreeNode<T> CreateNode(T tag)


{
return new TreeNode<T>(tag);
}

public void Sort()


{
rootNode.Sort();
}
}

public class TreeNode<T> : IComparable<TreeNode<T>>


where T : IComparable<T>
{
private T tag = default(T);
public T Tag
{
get { return tag; }
set { tag = value; }
}

private List<TreeNode<T>> nodes = new List<TreeNode<T>>();


public List<TreeNode<T>> Nodes
{
get { return nodes; }
set { nodes = value; }
}

public TreeNode(T tag)


{
this.tag = tag;
}

public void Sort()


{
nodes.Sort();
foreach (TreeNode<T> node in nodes)
{
node.Sort();
}
}

public int CompareTo(TreeNode<T> treeNode)


{
return this.tag.CompareTo(treeNode.Tag);
}
}

public class Person : IComparable<Person>


{
private string name = null;
public string Name

Using generic– By Ludwig Stuyck Page 31 of 51


Ctg Technical Articles

{
get { return name; }
set { name = value; }
}

private string location = null;


public string Location
{
get { return location; }
set { location = value; }
}

private int age = 0;


public int Age
{
get { return age; }
set { age = value; }
}

public Person(string name, string location, int age)


{
this.name = name;
this.location = location;
this.age = age;
}

public int CompareTo(Person person)


{
return this.name.CompareTo(person.Name);
}
}

public class PersonCollection : CollectionBase


{
public Person this[int index]
{
get
{
return (Person)List[index];
}
set
{
List[index] = value;
}
}

public int Add(Person person)


{
return List.Add(person);
}

public void Insert(int index, Person person)


{
List.Insert(index, person);
}

Using generic– By Ludwig Stuyck Page 32 of 51


Ctg Technical Articles

public void Remove(Person person)


{
List.Remove(person);
}

public bool Contains(Person person)


{
return List.Contains(person);
}

// Add other type-safe methods here


}

}
}

Constructor constraints
Suppose you want to instantiate a new generic object inside a generic class. You
have to be sure that the type that is passed actually has a public default
constructor. You can enforce this by using the new()constraint. Let’s have a look
at the following generic class:

public class GenericCollection<T>


{
T instance = new T();
}

When you try to compile this, you’ll see that there’s a compiler error ‘Cannot
create an instance of the variable type 'T' because it does not have the new ()
constraint’. So the compiler is not sure that type T has a public default
constructor, because we have not enforced it. We can do so as follows:

public class GenericCollection<T> where T : new()


{
T instance = new T();
}

Reference/Value Type Constraints


It’s possible to specify that a generic type has to be a reference type (a class) by
using the class keyword, or a value type (such as an int, bool, enum, struct, …)
by using the struct keyword. Example:

public class GenericCollection<T> where T : class

Using generic– By Ludwig Stuyck Page 33 of 51


Ctg Technical Articles

{
}

Other generic collections


We’ve used the generic List<> collection in our previous examples, but there
are other generic collection types that can be used out of the box.

SortedList<>
Represents a sorted list.

Queue<>
Represents a first-in, first-out collection.

Stack<>
Represents a last-in, first-out collection.

Dictionary<>
Represents a collection that associates a key to a value.

SortedDictionary<>
Represents a sorted collection that associates a key to a value.

LinkedList<>
Represents a double linked list.

Generics and databinding


Databinding is an important feature in windows and web programming. In this
part I’ll demonstrate how you can implement databinding of a generic collection
with a Windows Form DataGridView control, but of course this technique will
work with other controls too. In .NET 2.0 databinding has become rather easy to
do, as you will see.

Create the business object


To start, create a new Windows Forms project and call it Generics5. Add the
Person class again:

public class Person


{
private string name = string.Empty;
public string Name

Using generic– By Ludwig Stuyck Page 34 of 51


Ctg Technical Articles

{
get { return name; }
set { name = value; }
}

private string location = string.Empty;


public string Location
{
get { return location; }
set { location = value; }
}

private int age = 0;


public int Age
{
get { return age; }
set { age = value; }
}

public Person(string name, string location, int age)


{
this.name = name;
this.location = location;
this.age = age;
}
}

Now don’t forget to build the project at this point – otherwise the Person class
will not show up in the following step.

Register class as a data source


The next thing we need to do is to register the Person class so that our project
recognizes it as a data source. Select Data | Add New Data Source… in the
Visual Studio 200 menu:

Then select Object, because we are going to add our custom type as a data
source.

Using generic– By Ludwig Stuyck Page 35 of 51


Ctg Technical Articles

Click the Next button, and the following window will appear:

Using generic– By Ludwig Stuyck Page 36 of 51


Ctg Technical Articles

Select the Person class, and click the Finish Button. As you will see, Visual
Studio has added a DataSources folder to the project with a
Person.datasource configuration file, which is used to store generic object
data source configuration information:

Create a DataGridView and its columns


To display the data, drop a DataGridView control called dataGridView on
the form. In the Load event of the form we’ll add code to create a collection and
bind it to the grid. First, create some Person objects:

// Create some persons


Person person1 = new Person("Ludwig Stuyck", "Zaventem", 33);
Person person2 = new Person("Leen Rans", "Zaventem", 25);
Person person3 = new Person("Paulien Rans", "Rotselaar", 2);

Then, create a collection of these persons by using a BindingList generic


collection:

// Create a collection of persons


BindingList<Person> persons = new BindingList<Person>();
persons.Add(person1);
persons.Add(person2);
persons.Add(person3);

Now create a BindingSource object and set its data source to the collection we
just created:

// Create a binding source and set its datasource to the collection


BindingSource bindingSource = new BindingSource();
bindingSource.AllowNew = true;
bindingSource.DataSource = persons;

Next, run the application:

Using generic– By Ludwig Stuyck Page 37 of 51


Ctg Technical Articles

With this little piece of code we already have setup databinding! You can modify
and delete a field in the grid and it will be reflected in the data source (and vice
versa). However, if you now try to add a new row, you will get an error
‘Constructor on type not found’. This is because we have no default constructor
in our Person class, so we’ll add it:

public class Person


{
public Person()
{
}


}

And now you can add rows too.

Adding a BindingNavigator
Drop a BindingNavigator control to the form and call it
bindingNavigator, and set the DockState of the DataGridView to Fill:

Using generic– By Ludwig Stuyck Page 38 of 51


Ctg Technical Articles

Then, set the datasource of the BindingNavigator to the BindingSource object we


created:

// Set binding source of navigator


bindingNavigator.BindingSource = bindingSource;

Now run the application again. You will have full navigation support:

Adding sorting functionality


BindingList gives us out of the box add, delete and modify functionality.
However, to allow sorting in the grid we need to extend the BindingList class
and implement sorting ourselves. Add a new class, call it CustomBindingList
and make it inherit from the generic BindingList<T>:

Using generic– By Ludwig Stuyck Page 39 of 51


Ctg Technical Articles

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace Generics5
{
public lass CustomBindingList<T> : BindingList<T>
{
}
}

To indicate that the BindingList supports sorting, we override the


SupportsSortingCore method:

public class CustomBindingList<T> : BindingList<T>


{
protected override bool SupportsSortingCore
{
get { return true; }
}
}

Of course, now we still have to implement the actual sorting mechanism. To do


that, we need to override the ApplySortCore method. In this method we have
to sort the internal collection (which is of type List) using the specified property
and direction. Sorting a List comes down to calling the Sort method of the
List, which takes an IComparer object as a parameter. So what we will have
to do is to create our own generic class that implements the IComparer interface
and allows us to sort any property – well, we actually don’t have to do it
ourselves anymore because such generic property comparer has already been
written by Rockford Lhotka from Microsoft (http://www.lhotka.net), so add a
class called PropertyComparer and implement it as follows:

public class PropertyComparer<T> :


System.Collections.Generic.IComparer<T>
{

// PropertyComparer builds on a comparison algorithm based built


// by Rockford Lhotka and turns it into a generic property
// comparer for any type.
private PropertyDescriptor _property;
private ListSortDirection _direction;

public PropertyComparer(PropertyDescriptor property,


ListSortDirection direction)
{
_property = property;
_direction = direction;

Using generic– By Ludwig Stuyck Page 40 of 51


Ctg Technical Articles

#region IComparer<T>

public int Compare(T xWord, T yWord)


{
// Get property values
object xValue = GetPropertyValue(xWord, _property.Name);
object yValue = GetPropertyValue(yWord, _property.Name);

// Determine sort order


if (_direction == ListSortDirection.Ascending)
{
return CompareAscending(xValue, yValue);
}
else
{
return CompareDescending(xValue, yValue);
}
}

public bool Equals(T xWord, T yWord)


{
return xWord.Equals(yWord);
}

public int GetHashCode(T obj)


{
return obj.GetHashCode();
}

#endregion

// Compare two property values of any type


private int CompareAscending(object xValue, object yValue)
{
int result;

// If values implement IComparer


if (xValue is IComparable)
{
result = ((IComparable)xValue).CompareTo(yValue);
}
// If values don't implement IComparer but are equivalent
else if (xValue.Equals(yValue))
{
result = 0;
}
// Values don't implement IComparer and are not equivalent,
// so compare as string values
else result = Value.ToString().CompareTo(yValue.ToString());

// Return result
return result;
}

Using generic– By Ludwig Stuyck Page 41 of 51


Ctg Technical Articles

private int CompareDescending(object xValue, object yValue)


{
// Return result adjusted for ascending or descending sort
// order ie multiplied by 1 for ascending or -1 for
// descending
return CompareAscending(xValue, yValue) * -1;
}

private object GetPropertyValue(T value, string property)


{
// Get property
PropertyInfo propertyInfo =
value.GetType().GetProperty(property);

// Return value
return propertyInfo.GetValue(value, null);
}
}

Now we can use this PropertyComparer in our ApplySortCore method:

public class CustomBindingList<T> : BindingList<T>


{
private bool isSorted = false;
private PropertyDescriptor sortProperty = null;
private ListSortDirection sortDirection =
ListSortDirection.Ascending;

protected override void ApplySortCore(


PropertyDescriptor property, ListSortDirection direction)
{
// Get list to sort
List<T> items = this.Items as List<T>;

if (items != null)
{
PropertyComparer<T> propertyComparer
= new PropertyComparer<T>(property, direction);
items.Sort(propertyComparer);
isSorted = true;
}
else
{
isSorted = false;
}

sortProperty = property;
sortDirection = direction;
}

protected virtual bool SupportsSortingCore


{
get { return true; }

Using generic– By Ludwig Stuyck Page 42 of 51


Ctg Technical Articles

}
}

After a sort we need to let the bound controls know that sort order has changed,
which is a list-wide change. We do this by calling the OnListChanged method
after the sorting algorithm:

protected override void ApplySortCore(PropertyDescriptor property,


ListSortDirection direction)
{
// Get list to sort
List<T> items = this.Items as List<T>;

if (items != null)
{
PropertyComparer<T> propertyComparer
= new PropertyComparer<T>(property, direction);
items.Sort(propertyComparer);
isSorted = true;
}
else
{
isSorted = false;
}

sortProperty = property;
sortDirection = direction;

this.OnListChanged(new
ListChangedEventArgs(ListChangedType.Reset, -1));
}

Next thing to do is to override the IsSortedCore property, to make sure that the
bound controls know that sorting is done:

protected override bool IsSortedCore


{
get { return isSorted; }
}

And we do the same thing for SortDirectionCore and SortPropertyCore:

protected override ListSortDirection SortDirectionCore


{
get { return sortDirection; }
}

protected override PropertyDescriptor SortPropertyCore


{
get { return sortProperty; }
}

Using generic– By Ludwig Stuyck Page 43 of 51


Ctg Technical Articles

When sorting is removed, we also need to set the internal state to reflect this by
overriding the RemoveSortCore method:

protected override void RemoveSortCore()


{
isSorted = false;
}

We’re almost there. Of course, we now need to use our CustomBindingList as


a data source for our grid:

// Create some persons


Person person1 = new Person("Ludwig Stuyck", "Zaventem", 33);
Person person2 = new Person("Leen Rans", "Zaventem", 25);
Person person3 = new Person("Paulien Rans", "Rotselaar", 2);

// Create a collection of persons


CustomBindingList<Person> persons = new CustomBindingList<Person>();
persons.Add(person1);
persons.Add(person2);
persons.Add(person3);

// Create a binding source and set its datasource to the collection


BindingSource bindingSource = new BindingSource();
bindingSource.AllowNew = true;
bindingSource.DataSource = persons;

dataGridView.DataSource = bindingSource;

// Set binding source of navigator


bindingNavigator.BindingSource = bindingSource;

And if you now start the application, and click on a column, it will be sorted:

Using generic– By Ludwig Stuyck Page 44 of 51


Ctg Technical Articles

Adding searching functionality


As with sorting, we also have to implement our own searching mechanism into
our custom binding list. First, override the SupportsSearchingCore method,
to indicate that the BindingList supports searching:

protected override bool SupportsSearchingCore


{
get { return true; }
}

Next we have to override FindCore and implement our own searching


mechanism: we have to look in the internal collection for a specific property
holding a specific value. To do this, we’ll use the predicate-technique discussed
previously in this article:

protected override int FindCore(PropertyDescriptor property, object key)


{
if (property == null)
{
// No property specified
return -1;
}

// Get list to search


List<T> items = this.Items as List<T>;

PropertyValueFilter<T> propertyValueFilter = new


PropertyValueFilter<T>(property, (string)key);
Predicate<T> filterByValue = new
Predicate<T>(propertyValueFilter.FilterByValue);
return items.FindIndex(filterByValue);
}

As you see, it uses a PropertyValueFilter class that acts as a predicate, so


add a class called PropertyValueFilter and implement it as follows:

public class PropertyValueFilter<T>


{
private string value = string.Empty;
private PropertyDescriptor property = null;

public PropertyValueFilter(
PropertyDescriptor property, string value)
{
this.value = value;
this.property = property;
}

Using generic– By Ludwig Stuyck Page 45 of 51


Ctg Technical Articles

public bool FilterByValue(T t)


{
string v = (string)property.GetValue(t);
return value.Equals(v);
}
}

In fact, we now have done all that is necessary to support searching, so let’s
search! Add a TextBox to the bindingNavigator control and call it
toolStripSearchNameTextBox. Also add a Button and call it
toolStripSearchButton. Double click on the button to start implementing
it’s Click event handler:

private void toolStripSearchButton_Click(object sender, EventArgs e)


{
int index = bindingSource.Find("Name",
toolStripSearchNameTextBox.Text);
if (index > -1)
{
foreach (DataGridViewRow row in dataGridView.Rows)
{
row.Selected = false;
}
dataGridView.Rows[index].Selected = true;
}
}

Note: move the definition of the bindingSource to a class level definition to


make it accessible in the event handler.

Now start the application, type in a name in the text box, click the button and if
the name is in the grid, its row will be selected:

Using generic– By Ludwig Stuyck Page 46 of 51


Ctg Technical Articles

Serializing and deserializing a generic


collection
Sometimes it would be handy if we have the possibility to persist a collection to a
file in order to retrieve it later. This is quite simple to achieve, as a matter a fact
we will implement it in our CustomBindingList so that it works for every
type. Add a Save and Load method in the CustomBindingList class:

public void Save(string filename)


{
BinaryFormatter formatter = new BinaryFormatter();
using (FileStream fileStream = new FileStream(filename,
FileMode.Create))
{
formatter.Serialize(fileStream, (List<T>)this.Items);
}
}

public void Load(string filename)


{

this.ClearItems();

if (File.Exists(filename))
{
BinaryFormatter formatter = new BinaryFormatter();

Using generic– By Ludwig Stuyck Page 47 of 51


Ctg Technical Articles

using (FileStream fileStream = new FileStream(filename,


FileMode.Open))
{
((List<T>)this.Items).AddRange(
(IEnumerable<T>)formatter.Deserialize(fileStream));
}
}

this.OnListChanged(new ListChangedEventArgs(
ListChangedType.Reset, -1));
}

Don’t forget to add the following two using statements on top:

using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

One more thing: in order to be able to serialize a collection of objects, the object
itself should be marked as serializable. So modify the Person class and
decorate it with the Serializable attribute:

[Serializable]
public class Person
{
public Person()
{
}


}

Generics and LINQ


LINQ (Language Integrated Query) brings the power of queries into C#. For the
example I have used the CTP of may 2006.

Create a new LINQ console application and call it Generics6:

Using generic– By Ludwig Stuyck Page 48 of 51


Ctg Technical Articles

Add a new class called Person, as we did in previous examples.

Now we will write code to create a collection of Person objects:

using System;
using System.Collections.Generic;
using System.Text;
using System.Data.DLinq;
using System.Xml.XLinq;
using System.Query;

namespace Generics6
{
class Program
{
static void Main(string[] args)
{
// Create some persons
Person person1 =
new Person("Ludwig Stuyck", "Zaventem", 33);
Person person2 = new Person("Leen Rans", "Zaventem", 25);
Person person3 = new Person("Paulien Rans", "Rotselaar", 2);

// Create a collection of persons

Using generic– By Ludwig Stuyck Page 49 of 51


Ctg Technical Articles

List<Person> persons = new List<Person>();


persons.Add(person1);
persons.Add(person2);
persons.Add(person3);
}
}
}

Now let’s use LINQ to query this collection and find out what persons live in
Zaventem:

// Find all persons who live in Zaventem


var q = from c in persons
where c.Location == "Zaventem"
select c;

foreach(var val in q)
{
Console.WriteLine("{0},{1}, {2}", val.Name, val.Location, val.Age);
}

The result is:

About this article


Copyright © 2006 CTG. ALL RIGHTS RESERVED. Duplication is prohibited
without written permission.

Thanks to Pascal Desmet, Jort Marievoet, Bart Kuppens, Olivier Van Hege, Dick
Bevernage, Annelies Aerts and James Curran.

About Ctg
CTG was founded in 1966 in the USA and expanded to Europe in 1976. Our IT
professionals deliver our services through a network of ISO-certified sites in
North America and Europe. CTG Europe provides services in Belgium,
Luxembourg, France, and the United Kingdom.

Using generic– By Ludwig Stuyck Page 50 of 51


Ctg Technical Articles

To succeed in today's business world, companies need to use cutting-edge


technology. CTG empowers its clients to rapidly leverage today's innovative IT
and Web technology across their enterprises.

Website: http://www.ctg.com

Using generic– By Ludwig Stuyck Page 51 of 51

You might also like