CTG Articles Generics

Embed Size (px)

Citation preview

  • 8/14/2019 CTG Articles Generics

    1/51

    Page 1 of 51

    Using genericsBy Ludwig Stuyck ([email protected])Last update: 28 February 2007

    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 classicalarrays and .NET collections, and then I show you how

    generics provide a nice and elegant way of creating flexibleand performant strongly typed collections with sortingcapabilities. I also explain the use of predicates to implementcustom behavior, and how you can create your own genericclasses. Along the way constraints, generic methods,

    properties and interfaces are explained. Then I demonstratehow you can data bind a generic collection to a DataGridViewcontrol and how to serialize/deserialize the collection. Finally I

    use the power of LINQ to query a generic collection.

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

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

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

    However, they are very limited: they cant grow (they are rather static), and youdont have any convenience methods (like sorting, iterating ). In some cases

    arrays will be sufficient, but mostly you need more flexibility. And moreflexibility we get with the .NET collection classes, like ArrayList.

    Creating a simple collection with ArrayListYou have probably already used an ArrayList to create a collection of objects.Its a very simple and fast method, and it does the job, right? Lets say, forexample, you need to create a collection of numbers and calculate the sum.

  • 8/14/2019 CTG Articles Generics

    2/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 2 of 51

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

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.Collections;

    namespace Generics1

    {

    classProgram

    {

    staticvoid Main(string[] args)

    {

    // Create collection of numbers

    ArrayList numbers = newArrayList();

    numbers.Add(1);

    numbers.Add(5);

    // Calculate total sumint 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 Ill tell you why. First of all, an ArrayListis not strongly typed. And if you look at the signature of the Add method, youllsee 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 InvalidCastExceptionis thrown because in the loop it will try to add the string to a number. Of course,

  • 8/14/2019 CTG Articles Generics

    3/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 3 of 51

    we know that adding a string is not valid, but the compiler will allow it anyway:theres no compile-time checking possible. Also, theres a lot of casting going on:when you add a number to the collection, theres an implicit upcast to an object;and visa versa: in the loop the objects are unboxed again to integers. Boxing andunboxing are very time-consuming operations and should be avoided, because

    they degrade performance. This effect of boxing/unboxing can be quitesignificant in scenarios where you must iterate large collections.

    Creating a strongly typed collectionIn version 1.1 of the .NET framework, we could make our own strongly typedcollections by creating classes that inherit from existing framework collectionclasses (like ArrayList), or by creating classes that inherit from abstract classes(like CollectionBase or DictionaryBase). For example, lets create astrongly typed collection for a business object Person. Create a new console

    application, call itGenerics2

    , and add a new class calledPerson

    :publicclassPerson

    {

    privatestring name = string.Empty;

    publicstring Name

    {

    get { return name; }

    set { name = value; }

    }

    privatestring location = string.Empty;

    publicstring Location

    {

    get { return location; }

    set { location = value; }

    }

    privateint age = 0;

    publicint Age

    {

    get { return age; }

    set { age = value; }

    }

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

    {

    this.name = name;this.location = location;

    this.age = age;

    }

    }

  • 8/14/2019 CTG Articles Generics

    4/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 4 of 51

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

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.Collections;

    publicclassPersonCollection : CollectionBase

    {

    publicPersonthis[int index]

    {

    get

    {

    return (Person)List[index];

    }

    set

    {

    List[index] = value;

    }}

    publicint Add(Person person)

    {

    return List.Add(person);

    }

    publicvoid Insert(int index, Person person)

    {

    List.Insert(index, person);

    }

    publicvoid Remove(Person person)

    {

    List.Remove(person);

    }

    publicbool 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

    {

  • 8/14/2019 CTG Articles Generics

    5/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 5 of 51

    classProgram

    {

    staticvoid Main(string[] args)

    {

    // Create some persons

    Person person1 =

    newPerson("Ludwig Stuyck", "Zaventem", 33);

    Person person2 = newPerson("Leen Rans", "Zaventem", 25);

    // Create collection and add customers

    PersonCollection persons = newPersonCollection();

    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 persons collection.If we would add another type of object, the compiler would give an error. So wehave created a strongly typed collection that only accepts Person objects;however, theres still a lot of casting going on in our PersonCollection class.As expected, the output is:

    GenericsTime 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 ingeneral, its a code template that can be applied to use the same code, usingvarious types. They are also called parameterized types or parametricpolymorphism. Generics have a number of important advantages:

  • 8/14/2019 CTG Articles Generics

    6/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 6 of 51

    Type safety: generic classes work with a well-determined object type.Binary code reuse: generic code is generated at run-time, so differentusages 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 canhandle.

    Lets explain this further with an example.

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

    using System;using System.Collections.Generic;

    using System.Text;

    namespace Generics3

    {

    classProgram

    {

    staticvoid Main(string[] args)

    {

    // Create some persons

    Person person1 =

    newPerson("Ludwig Stuyck", "Zaventem", 33);

    Person person2 = newPerson("Leen Rans", "Zaventem", 25);

    // Create collection and add persons

    List persons = newList();

    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:

  • 8/14/2019 CTG Articles Generics

    7/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 7 of 51

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

    List persons = newList();

    You can immediately see the huge advantage because we dont have to write ourcustom collection classes (like PersonCollection) anymore! Moreover, theresno need for casting objects.

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

    // Create collection and add numbers

    List numbers = newList();

    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 collectionSuppose we want to find a specific person in the persons list. We can easily do sothanks to predicates. A predicate represents the method that defines a set ofcriteria and determines whether the specified object meets those criteria. Lets seehow it works in the following example.

    First, we create a PersonNameFilter class that implements a methodFilterByName. This class contains a name variable that contains the name of thecustomer that we want to find.

    publicclassPersonNameFilter

    {

  • 8/14/2019 CTG Articles Generics

    8/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 8 of 51

    privatestring name = string.Empty;

    public PersonNameFilter(string name)

    {

    this.name = name;

    }

    publicbool 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 personsList persons = new List();

    persons.Add(person1);

    persons.Add(person2);

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

    // for the specified name

    PersonNameFilter personNameFilter =

    newPersonNameFilter("Ludwig Stuyck");

    // Create a new predicate, that uses the FilterByName method

    // to determine whether the person has been found

    Predicate filterByName = new

    Predicate(personNameFilter.FilterByName);

    // Find the person in the collection

    Person foundPerson = persons.Find(filterByName);

    When the Find method is called, the FilterByName method is executed foreach 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 theFind method. When the method never returns true (so the person that issearched for is not in the collection), then the Find method will return null.

    Finding an object collection in a List

    collectionSuppose we need to look for persons that live in Zaventem. We create a newclass PersonLocationFilter, which contains a methodFilterByLocation:

    publicclassPersonLocationFilter

  • 8/14/2019 CTG Articles Generics

    9/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 9 of 51

    {

    privatestring location = string.Empty;

    public PersonLocationFilter(string location)

    {

    this.location = location;

    }

    publicbool FilterByLocation(Person person)

    {

    return person.Location.Equals(location);

    }

    }

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

    // Create a new person location filter, that will look for the specified

    location

    PersonLocationFilter personLocationFilter = newPersonLocationFilter("Zaventem");

    // Create a new predicate, that uses the FilterByLocation method

    // to determine whether the person has been found

    Predicate filterByLocation = new

    Predicate(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 foundPersons = persons.FindAll(filterByLocation);

    This method will return a collection of Person objects, instead of a singlePerson object. When the FindAll method is executed, theFilterByLocation method is executed for each person in the collection. Everytime the FilterByLocation method returns true, the current Person object isadded to the resulting collection.

    Iterating a list to perform operationsSuppose 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;

  • 8/14/2019 CTG Articles Generics

    10/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 10 of 51

    }

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

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

    publicclassPersonAgeCalculator

    {

    privateint oldestAge = 0;

    publicint OldestAge

    {

    get { return oldestAge; }

    set { oldestAge = value; }

    }

    publicvoid 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 theperson collection:

    // Create a new person age calculator

    PersonAgeCalculator personAgeCalculator = newPersonAgeCalculator();

    // Create a new action, that corresponds to the

    // CalculateOldestAge method

    Action action = new

    Action(personAgeCalculator.CalculateOldestAge);

    // Execute the action for each person in the collectionpersons.ForEach(action);

    Console.WriteLine("{0}", personAgeCalculator.OldestAge);

    Sorting generic listsSorting 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 comparethemselves. In our example, the Person objects need to know how to compareeach other. We can do so by implementing the IComparable interface, meaning

    we have to implement the CompareTo method:

    publicclassPerson : IComparable

    {

    private string name = null;

    public string Name

    {

  • 8/14/2019 CTG Articles Generics

    11/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 11 of 51

    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;

    }

    publicint CompareTo(object obj)

    {

    Person person = (Person)obj;

    returnthis.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 persons = new List();

    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);

  • 8/14/2019 CTG Articles Generics

    12/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 12 of 51

    }

    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 thatthere was still casting needed in the CompareTo method, when we implemented

    the IComparable interface. Indeed, the IComparable interface forces us toimplement the CompareTo method, which takes an object as a parameter:

    publicint CompareTo(object obj)

    {

    // Cast object back to a Person first

    Person person = (Person)obj;

    returnthis.name.CompareTo(person.Name);

    }

    As we have said before, we should avoid casting, and thats what well do next.The solution to this problem is generic interfaces. Instead of implementing theIComparable interface, we will implement its generic counter part, theIComparable interface:

    public class Person : IComparable

    {

    private string name = null;

  • 8/14/2019 CTG Articles Generics

    13/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 13 of 51

    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;

    }

    publicint CompareTo(Person person)

    {

    returnthis.name.CompareTo(person.Name);

    }

    }

    As a consequence, the CompareTo method does not have an object parameteranymore, but a parameter of type Person, avoiding any casting.

    Extending sortingIn the previous example we implemented sorting. But, we did not have thepossibility to specify on which property the sort should be done. It would be niceif we could sort the collection by another property. First of all, we will create ourown custom implementation of IComparer. Well add a new classPersonComparer, that implements the generic interface IComparer.This class knows about Person objects, and knows how to sort them. But beforewe do that, create an enumeration with supported comparison types:

    // Enumeration of comparison types

    publicenumPersonComparisonType

    {

  • 8/14/2019 CTG Articles Generics

    14/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 14 of 51

    Name,

    Location,

    Age

    };

    And now, implement the PersonComparer class:

    publicclassPersonComparer : IComparer

    {

    privatePersonComparisonType comparisonType =

    PersonComparisonType.Name;

    publicPersonComparisonType ComparisonType

    {

    get { return comparisonType; }

    set { comparisonType = value; }

    }

    public PersonComparer(PersonComparisonType comparisonType)

    {

    this.comparisonType = comparisonType;}

    publicbool Equals(Person lhs, Person rhs)

    {

    returnthis.Compare(lhs, rhs) == 0;

    }

    // Tell the Person objects to compare themselves

    publicint 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.Inthe class we implemented the method Compare, and here we use a new methodCompareTo of the Person objects to do the comparison, passing theComparisonType. So we will add this method to the Person class.

    public class Person : IComparable

    {

    private string name = null;

    public string Name

    {

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

    }

    private string location = null;

    public string Location

    {

    get { return location; }

  • 8/14/2019 CTG Articles Generics

    15/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 15 of 51

    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

    publicint CompareTo(Person person,

    PersonComparisonType comparisonType)

    {

    switch (comparisonType)

    {

    casePersonComparisonType.Name:

    returnthis.name.CompareTo(person.Name);

    casePersonComparisonType.Age:

    returnthis.age.CompareTo(person.Age);

    casePersonComparisonType.Location:

    returnthis.location.CompareTo(person.Location);

    }

    return 0;

    }

    }

    Depending on the passed comparison type, the correct properties are comparedto 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 persons = new List();

    persons.Add(person1);

  • 8/14/2019 CTG Articles Generics

    16/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 16 of 51

    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 =

    newPersonComparer(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 moreIn 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 bedescending Lets implement this. First, create a SortOrderType enumeration:

    // Enumeration of sorder order types

    publicenumSortOrderType

    {

    Ascending,

    Descending

    };

  • 8/14/2019 CTG Articles Generics

    17/51

  • 8/14/2019 CTG Articles Generics

    18/51

  • 8/14/2019 CTG Articles Generics

    19/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 19 of 51

    // Create the generic tree that will work with Person objects

    Tree personHierarchy = newTree();

    }

    Lets implement the tree now. A tree consists out of tree nodes that can workwith this type T, so well need a TreeNode class, which is also a generic class:

    publicclassTreeNode

    {

    }

    The top of the tree hierarchy is a root node, so well add a RootNode property tothe Tree class, so this is a generic property:

    public class Tree

    {

    privateTreeNode rootNode = default(TreeNode);

    publicTreeNode RootNode

    {

    get { return rootNode; }

    set { rootNode = value; }

    }

    }

    Note the default () operator, which returns the default value of a type. It can beused 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 personHierarchy = new Tree();

    // Create a tree node object, that will work with Person objects

    TreeNode grandFatherNode = newTreeNode();

    // 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 ageneric property:

    public class TreeNode

    {

    private T tag = default(T);

  • 8/14/2019 CTG Articles Generics

    20/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 20 of 51

    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 immediatelypass its assigned object of type T. So well add a constructor to the TreeNodeclass with one parameter:

    public class TreeNode

    {

    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 atree 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 personHierarchy = new Tree();

    // Create a person object

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

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

    TreeNode grandFatherNode

    = newTreeNode(grandFather);

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

    personHierarchy.RootNode = grandFatherNode;

    }

  • 8/14/2019 CTG Articles Generics

    21/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 21 of 51

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

    public class Tree

    { private TreeNode rootNode = default(TreeNode);

    public TreeNode RootNode

    {

    get { return rootNode; }

    set { rootNode = value; }

    }

    publicTreeNode CreateNode(T tag)

    {

    returnnewTreeNode(tag);

    }

    }

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

    static void Main(string[] args)

    {

    // Create the generic tree that will work with Person objects

    Tree personHierarchy = new Tree();

    // Create a person object

    Person grandFather = newPerson("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 nodepersonHierarchy.RootNode = personHierarchy.CreateNode(grandFather);

    }

    Theres something missing: our tree has a root node, but a root node should havechild nodes. So add a property Nodes to the TreeNode class, and this should be alist of tree nodes:

    public class TreeNode

    {

    private T tag = default(T);

    public T Tag

    {get { return tag; }

    set { tag = value; }

    }

    privateList nodes = newList();

    publicList Nodes

  • 8/14/2019 CTG Articles Generics

    22/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 22 of 51

    {

    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 personHierarchy = new Tree();

    // Create person objects

    Person grandFather = newPerson("Louis", "Zaventem", 92);Person son1 = newPerson("Michael", "Ukkel", 65);

    Person son2 = newPerson("Jan", "Leuven", 62);

    Person daughter = newPerson("Jennifer", "Zaventem", 57);

    Person grandson = newPerson("Kobe", "Steenokkerzeel", 26);

    Person grandDaughter = newPerson("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 treeNodeSon1 = personHierarchy.CreateNode(son1);

    personHierarchy.RootNode.Nodes.Add(treeNodeSon1);

    // son2 is child of root node

    TreeNode treeNodeSon2 = personHierarchy.CreateNode(son2);

    personHierarchy.RootNode.Nodes.Add(treeNodeSon2);

    // daughter is child of root node

    TreeNode treeNodedaughter =

    personHierarchy.CreateNode(daughter);

    personHierarchy.RootNode.Nodes.Add(treeNodedaughter);

    // grandson is child of daughterTreeNode treeNodegrandson =

    personHierarchy.CreateNode(grandson);

    treeNodedaughter.Nodes.Add(treeNodegrandson);

    // granddaughter is child of daughter

    TreeNode treeNodegrandDaughter =

    personHierarchy.CreateNode(grandDaughter);

  • 8/14/2019 CTG Articles Generics

    23/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 23 of 51

    treeNodedaughter.Nodes.Add(treeNodegrandDaughter);

    }

    We have now created a tree with a root node, this root node has three childnodes, 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 thats the whole idea of using generics: write once, use many.

    Derivation constraintsWe want to implement sorting capability in our tree. Sorting simply means thatwe need to sort each child node collection in the tree. But before we do this, firstwrite 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 personHierarchy = new Tree();

    // 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 treeNodeSon1 = personHierarchy.CreateNode(son1);

    personHierarchy.RootNode.Nodes.Add(treeNodeSon1);

  • 8/14/2019 CTG Articles Generics

    24/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 24 of 51

    // son2 is child of root node

    TreeNode treeNodeSon2 = personHierarchy.CreateNode(son2);

    personHierarchy.RootNode.Nodes.Add(treeNodeSon2);

    // daughter is child of root node

    TreeNode treeNodedaughter =

    personHierarchy.CreateNode(daughter);

    personHierarchy.RootNode.Nodes.Add(treeNodedaughter);

    // grandson is child of daughter

    TreeNode treeNodegrandson =

    personHierarchy.CreateNode(grandson);

    treeNodedaughter.Nodes.Add(treeNodegrandson);

    // granddaughter is child of daughter

    TreeNode treeNodegrandDaughter =

    personHierarchy.CreateNode(grandDaughter);

    treeNodedaughter.Nodes.Add(treeNodegrandDaughter);

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

    }

    publicstaticvoid PrintNode(TreeNode node, string spaces)

    {

    Console.WriteLine(spaces + node.Tag.Name);

    spaces += " ";

    foreach (TreeNode childNode in node.Nodes)

    {

    PrintNode(childNode, spaces);

    }

    }

    The result looks like:

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

  • 8/14/2019 CTG Articles Generics

    25/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 25 of 51

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

    However, theres a problem. A tree node does not now how to sort its collectionof tree nodes. Therefore we need to make sure that the TreeNode class

    implements the IComparable interface, because when we do so, it forces us toimplement the CompareTo method in the TreeNode class. In this method, thecomparison method of the objects of type T will be called, which means that eachTag property has to be compared. In our case, it means that each Person has tobe compared. The Person class knows how to compare itself, because we havealready implemented the IComparable interface previously.The first step is to make sure that tree nodes know how they have to compare toeach other, by implementing the IComparable genericinterface:

    public class TreeNode : IComparable{

    private T tag = default(T);

    public T Tag

    {

    get { return tag; }

    set { tag = value; }

    }

    private List nodes = new List();

    public List Nodes

    {

    get { return nodes; }

    set { nodes = value; }

    }

    public TreeNode(T tag)

    {

    this.tag = tag;

    }

    publicint CompareTo(TreeNode treeNode)

    {

    returnthis.tag.CompareTo(treeNode.Tag);

    }

    }

    As we have used the generic interface IComparable, we dontneed any casting in the CompareTo method.

    Next, as weve said, the tree should have a Sort method, that will call the rootcode Sort method:

  • 8/14/2019 CTG Articles Generics

    26/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 26 of 51

    public class Tree

    {

    private TreeNode rootNode = default(TreeNode);

    public TreeNode RootNode

    {

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

    }

    public TreeNode CreateNode(T tag)

    {

    return new TreeNode(tag);

    }

    publicvoid Sort()

    {

    rootNode.Sort();

    }

    }

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

    public class TreeNode : IComparable

    {

    private T tag = default(T);

    public T Tag

    {

    get { return tag; }

    set { tag = value; }

    }

    private List nodes = new List();

    public List Nodes

    {

    get { return nodes; }

    set { nodes = value; }

    }

    public TreeNode(T tag)

    {

    this.tag = tag;

    }

    publicvoid Sort()

    {

    nodes.Sort();

    foreach (TreeNode node in nodes)

    {

    node.Sort();

    }

    }

  • 8/14/2019 CTG Articles Generics

    27/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 27 of 51

    public int CompareTo(TreeNode treeNode)

    {

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

    }

    }

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

    public class Treewhere T : IComparable

    {

    }

    By doing this, we say that the type T must implement the IComparableinterface; 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 theTreeNode class. So we also need to make sure that the type T in the TreeNodeclass implements the IComparable interface:

    publicclassTreeNode : IComparable

    where T : IComparable{

    }

    Lets test our code:

    static void Main(string[] args)

    {

    // Create the generic tree that will work with Person objects

    Tree personHierarchy = new Tree();

    // 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);

  • 8/14/2019 CTG Articles Generics

    28/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 28 of 51

    // 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 treeNodeSon1 = personHierarchy.CreateNode(son1);

    personHierarchy.RootNode.Nodes.Add(treeNodeSon1);

    // son2 is child of root node

    TreeNode treeNodeSon2 = personHierarchy.CreateNode(son2);

    personHierarchy.RootNode.Nodes.Add(treeNodeSon2);

    // daughter is child of root node

    TreeNode treeNodedaughter =

    personHierarchy.CreateNode(daughter);

    personHierarchy.RootNode.Nodes.Add(treeNodedaughter);

    // grandson is child of daughterTreeNode treeNodegrandson =

    personHierarchy.CreateNode(grandson);

    treeNodedaughter.Nodes.Add(treeNodegrandson);

    // granddaughter is child of daughter

    TreeNode 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:

  • 8/14/2019 CTG Articles Generics

    29/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 29 of 51

    To be complete, heres the entire code:

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.Collections;

    namespace Generics

    {

    classProgram

    {

    staticvoid Main(string[] args)

    {

    // Create the generic tree that will work

    // with Person objects

    Tree personHierarchy = newTree();

    // Create person objects

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

    Person son1 = newPerson("Michael", "Ukkel", 65);

    Person son2 = newPerson("Jan", "Leuven", 62);

    Person daughter = newPerson("Jennifer", "Zaventem", 57);

    Person grandson = newPerson("Kobe", "Steenokkerzeel", 26);

    Person grandDaughter =

    newPerson("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 nodepersonHierarchy.RootNode =

    personHierarchy.CreateNode(grandFather);

    // Create child nodes and add them to the tree

    // son1 is child of root node

    TreeNode treeNodeSon1 =

  • 8/14/2019 CTG Articles Generics

    30/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 30 of 51

    personHierarchy.CreateNode(son1);

    personHierarchy.RootNode.Nodes.Add(treeNodeSon1);

    // son2 is child of root node

    TreeNode treeNodeSon2 =

    personHierarchy.CreateNode(son2);

    personHierarchy.RootNode.Nodes.Add(treeNodeSon2);

    // daughter is child of root node

    TreeNode treeNodedaughter =

    personHierarchy.CreateNode(daughter);

    personHierarchy.RootNode.Nodes.Add(treeNodedaughter);

    // grandson is child of daughter

    TreeNode treeNodegrandson =

    personHierarchy.CreateNode(grandson);

    treeNodedaughter.Nodes.Add(treeNodegrandson);

    // granddaughter is child of daughter

    TreeNode 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);

    }

    publicstaticvoid PrintNode(

    TreeNode node, string spaces)

    {

    Console.WriteLine(spaces + node.Tag.Name);

    spaces += " ";

    foreach (TreeNode childNode in node.Nodes)

    {

    PrintNode(childNode, spaces);

    }

    }

    publicclassTree where T : IComparable

    {

    privateTreeNode rootNode = default(TreeNode);

    publicTreeNode RootNode

    {

    get { return rootNode; }

  • 8/14/2019 CTG Articles Generics

    31/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 31 of 51

    set { rootNode = value; }

    }

    publicTreeNode CreateNode(T tag)

    {

    returnnewTreeNode(tag);

    }

    publicvoid Sort()

    {

    rootNode.Sort();

    }

    }

    publicclassTreeNode : IComparable

    where T : IComparable

    {

    private T tag = default(T);

    public T Tag

    {

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

    }

    privateList nodes = newList();

    publicList Nodes

    {

    get { return nodes; }

    set { nodes = value; }

    }

    public TreeNode(T tag)

    {

    this.tag = tag;

    }

    publicvoid Sort()

    {

    nodes.Sort();

    foreach (TreeNode node in nodes)

    {

    node.Sort();

    }

    }

    publicint CompareTo(TreeNode treeNode)

    {

    returnthis.tag.CompareTo(treeNode.Tag);}

    }

    publicclassPerson : IComparable

    {

    privatestring name = null;

    publicstring Name

  • 8/14/2019 CTG Articles Generics

    32/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 32 of 51

    {

    get { return name; }

    set { name = value; }

    }

    privatestring location = null;

    publicstring Location

    {

    get { return location; }

    set { location = value; }

    }

    privateint age = 0;

    publicint Age

    {

    get { return age; }

    set { age = value; }

    }

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

    {this.name = name;

    this.location = location;

    this.age = age;

    }

    publicint CompareTo(Person person)

    {

    returnthis.name.CompareTo(person.Name);

    }

    }

    publicclassPersonCollection : CollectionBase

    {

    publicPersonthis[int index]

    {

    get

    {

    return (Person)List[index];

    }

    set

    {

    List[index] = value;

    }

    }

    publicint Add(Person person)

    {return List.Add(person);

    }

    publicvoid Insert(int index, Person person)

    {

    List.Insert(index, person);

    }

  • 8/14/2019 CTG Articles Generics

    33/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 33 of 51

    publicvoid Remove(Person person)

    {

    List.Remove(person);

    }

    publicbool Contains(Person person)

    {

    return List.Contains(person);

    }

    // Add other type-safe methods here

    }

    }

    }

    Constructor constraintsSuppose you want to instantiate a new generic object inside a generic class. Youhave to be sure that the type that is passed actually has a public defaultconstructor. You can enforce this by using the new()constraint. Lets have a lookat the following generic class:

    publicclassGenericCollection

    {

    T instance = new T();

    }

    When you try to compile this, youll see that theres a compiler error Cannotcreate 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 defaultconstructor, because we have not enforced it. We can do so as follows:

    public class GenericCollectionwhere T : new()

    {

    T instance = new T();

    }

    Reference/Value Type ConstraintsIts possible to specify that a generic type has to be a reference type (a class) byusing the class keyword, or a value type (such as an int, bool, enum, struct, )by using the struct keyword. Example:

    publicclassGenericCollection where T : class

  • 8/14/2019 CTG Articles Generics

    34/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 34 of 51

    {

    }

    Other generic collectionsWeve 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.

    StackRepresents a last-in, first-out collection.

    DictionaryRepresents a collection that associates a key to a value.

    SortedDictionaryRepresents a sorted collection that associates a key to a value.

    LinkedListRepresents a double linked list.

    Generics and databindingDatabinding is an important feature in windows and web programming. In thispart Ill demonstrate how you can implement databinding of a generic collectionwith a Windows Form DataGridView control, but of course this technique willwork with other controls too. In .NET 2.0 databinding has become rather easy todo, as you will see.

    Create the business object

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

    publicclassPerson

    {

    privatestring name = string.Empty;

    publicstring Name

  • 8/14/2019 CTG Articles Generics

    35/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 35 of 51

    {

    get { return name; }

    set { name = value; }

    }

    privatestring location = string.Empty;

    publicstring Location

    {

    get { return location; }

    set { location = value; }

    }

    privateint age = 0;

    publicint 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 dont forget to build the project at this point otherwise the Person classwill 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 projectrecognizes it as a data source. Select Data | Add New Data Source in theVisual Studio 200 menu:

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

  • 8/14/2019 CTG Articles Generics

    36/51

  • 8/14/2019 CTG Articles Generics

    37/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 37 of 51

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

    Create a DataGridView and its columns

    To display the data, drop a DataGridView control called dataGridView onthe form. In the Load event of the form well add code to create a collection andbind it to the grid. First, create some Person objects:

    // Create some persons

    Person person1 = newPerson("Ludwig Stuyck", "Zaventem", 33);

    Person person2 = newPerson("Leen Rans", "Zaventem", 25);

    Person person3 = newPerson("Paulien Rans", "Rotselaar", 2);

    Then, create a collection of these persons by using a BindingList genericcollection:

    // Create a collection of personsBindingList persons = newBindingList();

    persons.Add(person1);

    persons.Add(person2);

    persons.Add(person3);

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

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

    BindingSource bindingSource = newBindingSource();

    bindingSource.AllowNew = true;

    bindingSource.DataSource = persons;

    Next, run the application:

  • 8/14/2019 CTG Articles Generics

    38/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 38 of 51

    With this little piece of code we already have setup databinding! You can modifyand delete a field in the grid and it will be reflected in the data source (and viceversa). However, if you now try to add a new row, you will get an errorConstructor on type not found. This is because we have no default constructor

    in our Person class, so well add it:

    publicclassPerson

    {

    public Person()

    {

    }

    }

    And now you can add rows too.

    Adding a BindingNavigator

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

  • 8/14/2019 CTG Articles Generics

    39/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 39 of 51

    Then, set the datasource of the BindingNavigator to the BindingSource object wecreated:

    // Set binding source of navigator

    bindingNavigator.BindingSource = bindingSource;

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

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

  • 8/14/2019 CTG Articles Generics

    40/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 40 of 51

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.ComponentModel;

    namespace Generics5

    {

    public lassCustomBindingList : BindingList

    {

    }

    }

    To indicate that the BindingList supports sorting, we override theSupportsSortingCore method:

    public classCustomBindingList : BindingList

    {

    protectedoverridebool SupportsSortingCore

    {

    get { returntrue; }}

    }

    Of course, now we still have to implement the actual sorting mechanism. To dothat, 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 propertyand direction. Sorting a List comes down to calling the Sort method of theList, which takes an IComparer object as a parameter. So what we will haveto do is to create our own generic class that implements the IComparer interfaceand allows us to sort any property well, we actually dont have to do itourselves anymore because such generic property comparer has already beenwritten by Rockford Lhotka from Microsoft (http://www.lhotka.net), so add aclass called PropertyComparer and implement it as follows:

    publicclassPropertyComparer :

    System.Collections.Generic.IComparer

    {

    // PropertyComparer builds on a comparison algorithm based built

    // by Rockford Lhotka and turns it into a generic property

    // comparer for any type.

    privatePropertyDescriptor _property;

    privateListSortDirection _direction;

    public PropertyComparer(PropertyDescriptor property,

    ListSortDirection direction)

    {

    _property = property;

    _direction = direction;

  • 8/14/2019 CTG Articles Generics

    41/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 41 of 51

    }

    #region IComparer

    publicint 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);

    }

    }

    publicbool Equals(T xWord, T yWord)

    {

    return xWord.Equals(yWord);

    }

    publicint GetHashCode(T obj)

    {

    return obj.GetHashCode();

    }

    #endregion

    // Compare two property values of any type

    privateint CompareAscending(object xValue, object yValue)

    {

    int result;

    // If values implement IComparer

    if (xValue isIComparable)

    {

    result = ((IComparable)xValue).CompareTo(yValue);

    }

    // If values don't implement IComparer but are equivalent

    elseif (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;

    }

  • 8/14/2019 CTG Articles Generics

    42/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 42 of 51

    privateint 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;

    }

    privateobject 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:

    publicclassCustomBindingList : BindingList

    {

    privatebool isSorted = false;

    privatePropertyDescriptor sortProperty = null;

    privateListSortDirection sortDirection =

    ListSortDirection.Ascending;

    protectedoverridevoid ApplySortCore(

    PropertyDescriptor property, ListSortDirection direction)

    {

    // Get list to sort

    List items = this.Items asList;

    if (items != null)

    {

    PropertyComparer propertyComparer

    = newPropertyComparer(property, direction);

    items.Sort(propertyComparer);

    isSorted = true;

    }

    else

    {

    isSorted = false;

    }

    sortProperty = property;

    sortDirection = direction;

    }

    protectedvirtualbool SupportsSortingCore

    {

    get { returntrue; }

  • 8/14/2019 CTG Articles Generics

    43/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 43 of 51

    }

    }

    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 items = this.Items as List;

    if (items != null)

    {

    PropertyComparer propertyComparer

    = new PropertyComparer(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 thebound controls know that sorting is done:

    protectedoverridebool IsSortedCore

    {

    get { return isSorted; }

    }

    And we do the same thing for SortDirectionCore and SortPropertyCore:

    protectedoverrideListSortDirection SortDirectionCore

    {get { return sortDirection; }

    }

    protectedoverridePropertyDescriptor SortPropertyCore

    {

    get { return sortProperty; }

    }

  • 8/14/2019 CTG Articles Generics

    44/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 44 of 51

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

    overriding the RemoveSortCore method:

    protectedoverridevoid RemoveSortCore()

    { isSorted = false;

    }

    Were almost there. Of course, we now need to use our CustomBindingList asa 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 persons = newCustomBindingList();

    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:

  • 8/14/2019 CTG Articles Generics

    45/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 45 of 51

    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:

    protectedoverridebool SupportsSearchingCore

    {

    get { returntrue; }

    }

    Next we have to override FindCore and implement our own searchingmechanism: we have to look in the internal collection for a specific propertyholding a specific value. To do this, well use the predicate-technique discussedpreviously in this article:

    protectedoverrideint FindCore(PropertyDescriptor property, object key)

    {

    if (property == null)

    {

    // No property specified

    return -1;

    }

    // Get list to search

    List items = this.Items asList;

    PropertyValueFilter propertyValueFilter = new

    PropertyValueFilter(property, (string)key);Predicate filterByValue = new

    Predicate(propertyValueFilter.FilterByValue);

    return items.FindIndex(filterByValue);

    }

    As you see, it uses a PropertyValueFilter class that acts as a predicate, soadd a class called PropertyValueFilter and implement it as follows:

    publicclassPropertyValueFilter

    {

    privatestring value = string.Empty;

    privatePropertyDescriptor property = null;

    public PropertyValueFilter(

    PropertyDescriptor property, string value)

    {

    this.value = value;

    this.property = property;

    }

  • 8/14/2019 CTG Articles Generics

    46/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 46 of 51

    publicbool 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 letssearch! Add a TextBox to the bindingNavigator control and call ittoolStripSearchNameTextBox. Also add a Button and call ittoolStripSearchButton. Double click on the button to start implementingits Click event handler:

    privatevoid 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 tomake it accessible in the event handler.

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

  • 8/14/2019 CTG Articles Generics

    47/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 47 of 51

    Serializing and deserializing a genericcollectionSometimes it would be handy if we have the possibility to persist a collection to afile in order to retrieve it later. This is quite simple to achieve, as a matter a factwe will implement it in our CustomBindingList so that it works for everytype. Add a Save and Load method in the CustomBindingList class:

    publicvoid Save(string filename)

    {

    BinaryFormatter formatter = new BinaryFormatter();

    using (FileStream fileStream = new FileStream(filename,

    FileMode.Create))

    {

    formatter.Serialize(fileStream, (List)this.Items);

    }

    }

    publicvoid Load(string filename)

    {

    this.ClearItems();

    if (File.Exists(filename))

    {

    BinaryFormatter formatter = new BinaryFormatter();

  • 8/14/2019 CTG Articles Generics

    48/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 48 of 51

    using (FileStream fileStream = new FileStream(filename,

    FileMode.Open))

    {

    ((List)this.Items).AddRange(

    (IEnumerable)formatter.Deserialize(fileStream));

    }

    }

    this.OnListChanged(newListChangedEventArgs(

    ListChangedType.Reset, -1));

    }

    Dont 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 anddecorate it with the Serializable attribute:

    [Serializable]

    public class Person

    {

    public Person()

    {

    }

    }

    Generics and LINQLINQ (Language Integrated Query) brings the power of queries into C#. For theexample I have used the CTP of may 2006.

    Create a new LINQ console application and call it Generics6:

  • 8/14/2019 CTG Articles Generics

    49/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 49 of 51

    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

    {

    classProgram

    {

    staticvoid Main(string[] args)

    {

    // Create some personsPerson person1 =

    newPerson("Ludwig Stuyck", "Zaventem", 33);

    Person person2 = newPerson("Leen Rans", "Zaventem", 25);

    Person person3 = newPerson("Paulien Rans", "Rotselaar", 2);

    // Create a collection of persons

  • 8/14/2019 CTG Articles Generics

    50/51

    Ctg Technical Articles

    Using generic By Ludwig Stuyck Page 50 of 51

    List persons = newList();

    persons.Add(person1);

    persons.Add(person2);

    persons.Add(person3);

    }

    }

    }

    Now lets use LINQ to query this collection and find out what persons live inZaventem:

    // 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 articleCopyright 2006 CTG. ALL RIGHTS RESERVED. Duplication is prohibitedwithout written permission.

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

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

  • 8/14/2019 CTG Articles Generics

    51/51

    Ctg Technical Articles

    To succeed in today's business world, companies need to use cutting-edgetechnology. CTG empowers its clients to rapidly leverage today's innovative ITand Web technology across their enterprises.

    Website: http://www.ctg.com