Section 1.2. Create Your Own Generic Collection


1.2. Create Your Own Generic Collection

.NET 2.0 provides a number of generic collection classes for lists, stacks, queues, dictionaries, etc. Typically, these are more than sufficient for your programming needs. But from time to time you might decide to create your own generic collection classes, such as when you want to provide those collections with problem-specific knowledge or capabilities that are simply not available in existing collections (for example, creating an optimized linked list, or adding generic collection semantics to another class you've created). It is a goal of the language and the Framework to empower you to create your own generic collection types.


Note: From time to time you will decide to create your own generic collection classes.

1.2.1. How do I do that?

The easiest way to create a generic collection class is to create a specific collection (for example, one that holds integers) and then replace the type (for example, int) with the generic type (for example, T).

Thus:

private int data;

becomes:

private T data;  // T is a generic Type Parameter

The generic type parameter (in this case, T) is defined by you when you create your collection class by placing the type parameter inside angle brackets (< >):

public class Node<T>


Tip: Many programmers use T for "type," but Microsoft recommends you use longer, more descriptive type names (for example, Node<DocumentType>).

Now you have defined a new type, "Node of T," which at runtime will become "Node of int" or node of any other type the compiler recognizes.

Example 1-2 creates a linked list of nodes of T, and then uses two instances of that generic list, each holding a different type of object.

Example 1-2. Creating your own generic collection
using System;     namespace GenericLinkedList {    public class Pilgrim    {       private string name;       public Pilgrim(string name)       {          this.name = name;       }       public override string ToString( )       {          return this.name;       }    }    public class Node<T>    {       // member fields       private T data;       private Node<T> next = null;           // constructor       public Node(T data)       {          this.data = data;       }           // properties       public T Data { get { return this.data; } }           public Node<T> Next       {          get { return this.next; }       }           // methods       public void Append(Node<T> newNode)       {          if (this.next =  = null)          {             this.next = newNode;          }          else          {             next.Append(newNode);          }       }       public override string ToString( )       {          string output = data.ToString( );              if (next != null)          {             output += ", " + next.ToString( );          }              return output;       }    }      // end class            public class LinkedList<T>    {       // member fields       private Node<T> headNode = null;           // properties           // indexer       public T this[int index]       {          get          {             int ctr = 0;             Node<T> node = headNode;                 while (node != null && ctr <= index)             {                if (ctr =  = index)                {                   return node.Data;                }                else                {                   node = node.Next;                }                    ++ctr;             } // end while             throw new ArgumentOutOfRangeException( );          }      // end get       }          // end indexer               // constructor       public LinkedList( )       {       }           // methods       public void Add(T data)       {          if (headNode =  = null)          {             headNode = new Node<T>(data);          }          else          {             headNode.Append(new Node<T>(data));          }       }       public override string ToString( )       {          if (this.headNode != null)          {             return this.headNode.ToString( );          }          else          {             return string.Empty;          }       }    }        class Program    {       static void Main(string[  ] args)       {          LinkedList<int> myLinkedList = new LinkedList<int>( );          for (int i = 0; i < 10; i++)          {             myLinkedList.Add(i);          }                  Console.WriteLine("Integers: " + myLinkedList);          LinkedList<Pilgrim> pilgrims = new LinkedList<Pilgrim>( );          pilgrims.Add(new Pilgrim("The Knight"));          pilgrims.Add(new Pilgrim("The Miller"));          pilgrims.Add(new Pilgrim("The Reeve"));          pilgrims.Add(new Pilgrim("The Cook"));              Console.WriteLine("Pilgrims: " + pilgrims);          Console.WriteLine("The fourth integer is " + myLinkedList[3]);          Pilgrim d = pilgrims[1];          Console.WriteLine("The second pilgrim is " + d);       }    } }

Output:

Integers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 Pilgrims: The Knight, The Miller, The Reeve, The Cook The fourth integer is 3 The second pilgrim is The Miller

1.2.2. What just happened?

You just created a generic linked list; one that is type-safe for any type of object you hold in the collection. In fact, one way to create a linked list such as this is to start by creating a type-specific linked list. This simple example works by defining a generic linked list whose head node is initialized to null:

public class LinkedList<T> {    private Node<T> headNode = null;    ... }

When you add data to the linked list, a new node is created and if there is no head node, that new node becomes the head; otherwise, append is called on the head node.

Each node checks to see if its next field is null (and thus the current node is the end of the list). If so, the current node appends the new node; otherwise, it passes the new node to the next member in the list.


Note: Creating collections with generics is far easier than you might imagine. The simplest way to approach the problem is to build a type-specific collection, and then replace the type with the generic <T>.

Notice that LinkedList is intentionally declared with the same generic type parameter as Node. Because they both use the same letter (T), the compiler knows that the type used to substitute for T in LinkedList will be the same type used to substitute for T in Node. This makes sense: a linked list of integers will hold nodes of integers.

1.2.3. What about...

...using generics with other code structures? Can I do that?

Sure; you also can use generics with structs, interfaces, delegates, and even methods.

1.2.4. Where can I learn more?

For more about creating your own class with generics, see the MSDN Help file, "Topic: Generics," as well as the article mentioned previously on O'Reilly's ONDotnet.com site at http://www.ondotnet.com/pub/a/dotnet/2004/05/17/liberty.html. Also, an open "Community Project to Develop the Best Public License Collection Classes for .NET" is available on the Wintellect site at http://www.wintellect.com/powercollections/.



Visual C# 2005(c) A Developer's Notebook
Visual C# 2005: A Developers Notebook
ISBN: 059600799X
EAN: 2147483647
Year: 2006
Pages: 95
Authors: Jesse Liberty

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net