Generics

If you're a C# programmer looking to add some more power to your code, you might want to consider learning how to use generics. This article, the first part of a three-part series, explains what they are and how to use them. It is excerpted from chapter four of the C# 3.0 Cookbook, Third Edition, written by Jay Hilyard and Stephen Teilhet (O'Reilly, 2008; ISBN: 059651610X). Copyright © 2008 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 3
December 11, 2008
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

4.0 Introduction

Generics are an extremely useful feature that allows you to write type safe and efficient collection- and pattern-based code. This aspect of generics is described in Recipes 4.1 and 4.2. With generics comes quite a bit of programming power, but with that power comes the responsibility to use it correctly. If you are considering converting your ArrayList, Queue, Stack, andHashtable objects to use their generic counterparts, consider reading Recipes 4.3, 4.4, and 4.9. As you will read, the conversion is not always simple and easy, and there are reasons why you might not want to do this conversion at all.

Some recipes in this chapter, such as Recipe 4.5, deal with other generic classes contained in the .NET Framework. Still, others deal with the operation of any generic type; see Recipes 4.1, 4.7, and 4.11.

4.1   Deciding When and Where to Use Generics

Problem

You want to use generic types in a new project or convert nongeneric types in an existing project to their generic equivalent. However, you do not really know why you would want to do this, and you do not know which nongeneric types should be converted to be generic.

Solution

In deciding when and where to use generic types, you need to consider several things:

  1. Will your type contain or be operating on various unspecified data types (e.g., a collection type)? If so, creating a generic type will offer several benefits over creating a nongeneric type. If your type will operate on only a single specific type, then you may not need to create a generic type.
  2. If your type will be operating on value types, so that boxing and unboxing operations will occur, you should consider using generics to prevent the boxing and unboxing operations.
  3. The stronger type checking associated with generics will aid in finding errors sooner (i.e., during compile time as opposed to runtime), thus shortening your bug-fixing cycle.
  4. Is your code suffering from “code bloat,” with you writing multiple classes to handle different data types on which they operate (e.g., a specializedArrayListthat stores onlyStreamReadersand another that stores onlyStreamWriters)? It is easier to write the code once and have it just work for each of the data types it operates on.
  5. Generics allow for greater clarity of code. By eliminating code bloat and forcing stronger type checking on your types, your code will be easier to read and understand.

Discussion

In most cases, your code will benefit from using a generic type. Generics allow for more efficient code reuse, faster performance, stronger type checking, and easier-to-read code.

See Also

The “Generics Overview” and “Benefits of Generics” topics in the MSDN documentation.

4.2 Understanding Generic Types

Problem

You need to understand how the .NET types work for generics and how Generic .NET types differ from regular .NET types. 

Solution

A couple of quick experiments can show the differences between regular .NET types and generic .NET types. When a regular .NET type is defined, it looks like the FixedSizeCollection type defined in Example 4-1.

FixedSizeCollection type defined in Example 4-1.A couple of quick experiments can show the differences between regular .NET types and generic .NET types. When a regular .NET type is defined, it looks like the FixedSizeCollection type defined in Example 4-1.

Example 4-1. FixedSizeCollection: a regular .NET type

public class FixedSizeCollection
{
   /// <summary>
   /// Constructor that increments static counter
   /// and sets the maximum number of items
   /// </summary>
   /// <param name="maxItems"></param> 
   public FixedSizeCollection(int maxItems)
   {
     
FixedSizeCollection.InstanceCount++;
      this.Items = new object[maxItems];
   }

   /// <summary>
   /// Add an item to the class whose type
   /// is unknown as only object can hold any type
   /// </summary>
  /// <param name="item">item to add</param>
   /// <returns>the index of the item added</returns>
   public int AddItem(object item)
   {
      if (this.ItemCount < this.Items.Length)
     
{
         this.Items[this.ItemCount] = item;
         return this.ItemCount++;
      }
      else
         throw new Exception("Item queue is full");
   }

   /// <summary>
   /// Get an item from the class
   /// </summary>
  /// <param name="index">the index of the item to get</param>
  /// <returns>an item of type object</returns>
  public object GetItem(int index)
   {
     
if (index >= this.Items.Length &&
          index >= 0)
          throw new ArgumentOutOfRangeException("index");

      return this.Items[index];
   }

   #region Properties
   /// <summary>
   /// Static instance counter hangs off of the Type for
   /// StandardClass
   /// </summary>
   public static int InstanceCount { get; set; }

   /// <summary>
  
/// The count of the items the class holds
   /// </summary>
   public int ItemCount { get; private set; }

   /// <summary>
   /// The items in the class
   /// </summary>
   private object[] Items { get; set; }
   #endregion // Properties

   /// <summary>
   /// ToString override to provide class detail
   /// </summary>
   /// <returns>formatted string with class details</returns>
   public override string ToString()
   {
     
return "There are " + FixedSizeCollection.InstanceCount.ToString() +
          " instances of " + this.GetType().ToString() +
          " and this instance contains " + this.ItemCount + " items...";
  
}
}

FixedSizeCollectionhas a static integer property variable,InstanceCount, which is incremented in the instance constructor, and aToString()override that prints out how many instances ofFixedSizeCollectionexist in thisAppDomain.FixedSizeCollectionalso contains an array ofobjects(Items), the size of which is determined by the item count passed in to the constructor. It implements methods to add and retrieve items (AddItem,GetItem) and a read-only property to get the number of items currently in the array (ItemCount).

TheFixedSizeCollection<T>type is a generic .NET type with the same static propertyInstanceCountfield, the instance constructor that counts the number of instantiations, and the overriddenToString()method to tell you how many instances there are of this type.FixedSizeCollection<T>also has anItemsarray property and methods corresponding to those inFixedSizeCollection, as you can see in Example 4-2.

Example 4-2. FixedSizeCollection<T>: a generic .NET type

/// <summary>
/// A generic class to show instance counting
/// </summary>
/// <typeparam name="T">the type parameter used for the array storage</typeparam>public class FixedSizeCollection<T>

   
/// <summary>
   /// Constructor that increments static counter and sets up internal storage
   /// </summary>
  /// <param name="items"></param>
   public FixedSizeCollection(int items)
   { 
       
FixedSizeCollection<T>.InstanceCount++;
       this.Items = new T[items];
   }

   /// <summary>
   /// Add an item to the class whose type
   /// is determined by the instantiating type
   /// </summary>
   /// <param name="item">item to add</param>
  /// <returns>the zero-based index of the item added</returns>
  public int AddItem(T item)
   {
      
if (this.ItemCount < this.Items.Length)
      
{
          this.Items[this.ItemCount] = item;
          return this.ItemCount++;
      
}
       else
          throw new Exception("Item queue is full");
   }

   /// <summary>
   /// Get an item from the class
   /// </summary>
   /// <param name="index">the zero-based index of the item to get</param>
   /// <returns>an item of the instantiating type</returns>
   public T GetItem(int index)
   {
      
if (index >= this.Items.Length &&  
           index >= 0)
           throw new ArgumentOutOfRangeException("index");

       return this.Items[index];
   }

   #region Properties
   /// <summary>
   /// Static instance counter hangs off of the
   /// instantiated Type for
   /// GenericClass
   /// </summary>
   public static int InstanceCount { get; set; }

   /// <summary>
   /// The count of the items the class holds
   /// </summary>
   public int ItemCount{ get; private set; }

   /// <summary>
   /// The items in the class
   /// </summary>
   private T[] Items { get; set; } 
   #endregion // Properties

   /// <summary>
   /// ToString override to provide class detail
   /// </summary>
   /// <returns>formatted string with class details</returns>
  public override string ToString()
   { 
       
return "There are " + FixedSizeCollection<T>.InstanceCount.ToString() +
           " instances of " + this.GetType().ToString() +
           " and this instance contains " + this.ItemCount + " items...";
  
}
}

Things start to get a little different withFixedSizeCollection<T>when you look at theItemsarray property implementation. TheItemsarray is declared as:

  private T[] Items { get; set; }

instead of:

  private object[] Items { get; set; }

TheItemsarray property uses the type parameter of the generic class (<T>) to determine what type of items are allowed.FixedSizeCollectionusesobjectfor theItemsarray property type, which allows any type to be stored in the array of items (since all types are convertible toobject), whileFixedSizeCollection<T>provides type safety by allowing the type parameter to dictate what types of objects are permitted. Notice also that the properties have no associated private backing field declared for storing the array. This is an example of using the new Automatically Implemented Properties in C# 3.0. Under the covers, the C# compiler is creating a storage element of the type of the property, but you don’t have to write the code for the property storage anymore if you don’t have specific code that has to execute when accessing the properties. To make the property read-only, simply mark theset;declarationprivate.

The next difference is visible in the method declarations ofAddItem andGetItem.AddItemnow takes a parameter of typeT, whereas inFixedSizeCollection, it took a parameter of typeobject.GetItem now returns a value of typeT, whereas inFixedSizeCollection, it returned a value of typeobject. These changes allow the methods inFixedSizeCollection<T>to use the instantiated type to store and retrieve the items in the array, instead of having to allow anyobject to be stored as inFixedSizeCollection:

  /// <summary>
  /// Add an item to the class whose type
  /// is determined by the instantiating type
  /// </summary>
 /// <param name="item">item to add</param>
  /// <returns>the zero-based index of the item added</returns>
  public int AddItem(T item)
  {
     if (this.ItemCount < this.Items.Length)
     { 
         
this.Items[this.ItemCount] = item;
        
return this.ItemCount++;
     }
     else
        
throw new Exception("Item queue is full");
  }

  /// <summary>
  /// Get an item from the class
  /// </summary>
  /// <param name="index">the zero-based index of the item to get</param>
  /// <returns>an item of the instantiating type</returns>
  public T GetItem(int index)
  {
     if (index >= this.Items.Length &&
         index >= 0)
         throw new ArgumentOutOfRangeException("index");

     return this.Items[index];
  }

This provides a few advantages. First and foremost is the type safety provided byFixedSizeCollection<T>for items in the array. It was possible to write code like this inFixedSizeCollection:

  // Regular class
  FixedSizeCollection C = new FixedSizeCollection(5);
  Console.WriteLine(C);

  string s1 = "s1";
  string s2 = "s2";
  string s3 = "s3";
  int i1 = 1;

  // Add to the fixed size collection (as object).
  C.AddItem(s1);
  C.AddItem(s2);
  C.AddItem(s3);
  // Add an int to the string array, perfectly OK.
  C.AddItem(i1);

ButFixedSizeCollection<T>will give a compiler error if you try the same thing:

  // Generic class
  FixedSizeCollection<string>gC = new FixedSizeCollection<string>(5); 
  Console.WriteLine(gC);

  string s1 = "s1";
  string s2 = "s2";
  string s3 = "s3";
  int i1 = 1;

  // Add to the generic class (as string).
  gC.AddItem(s1);
  gC.AddItem(s2);
  gC.AddItem(s3);
  // Try to add an int to the string instance, denied by compiler.
  // error CS1503: Argument '1': cannot convert from 'int' to 'string'
  //gC.AddItem(i1);

Having the compiler prevent this before it can become the source of runtime bugs is a very good thing.

It may not be immediately noticeable, but the integer is actuallyboxedwhen it is added to theobjectarray inFixedSizeCollection, as you can see in the IL for the call toGetItemonFixedSizeCollection:

  IL_0170:  ldloc.2
  IL_0171:  ldloc.s i1
 
IL_0173: box [mscorlib]System.Int32
  IL_0178:  callvirt instance int32
         CSharpRecipes.Generics/ FixedSizeCollection::AddItem(object)

This boxing turns the int, which is a value type, into a reference type (object) for storage in the array. This causes extra work to be done to store value types in theobjectarray.

There is a problem when you go to get an item back from the class in theFixedSizeCollectionimplementation. Take a look at howFixedSizeCollection. GetItem retrieves an item:

  // Hold the retrieved string.
  string sHolder;

  // Have to cast or get error CS0266:
  // Cannot implicitly convert type 'object' to 'string'...
  sHolder = (string)C.GetItem(1);

Since the item returned byFixedSizeCollection.GetItemis of typeobject, it needs to be cast to astring in order to get what you hope is astringfor index 1. It may not be a
string--all you know for sure is that it’s anobject--but you have to cast it to a more specific type coming out so you can assign it properly.

These are both fixed by theFixedSizeCollection<T>implementation. The unboxing is addressed; no unboxing is required, since the return type ofGetItem is the instantiated type, and the compiler enforces this by looking at the value being returned:

  // Hold the retrieved string.
  string sHolder;
  int iHolder;

  // No cast necessary
  sHolder = gC.GetItem(1);

  // Try to get a string into an int.
  // error CS0029: Cannot implicitly convert type 'string' to 'int'
  //iHolder = gC.GetItem(1);

In order to see one other difference between the two types, instantiate a few instances of each of them like so:

  // Regular class
  FixedSizeCollection A = new FixedSizeCollection(5);
  Console.WriteLine(A);
  FixedSizeCollection B = new FixedSizeCollection(5);
  Console.WriteLine(B);
  FixedSizeCollection C = new FixedSizeCollection(5);
  Console.WriteLine(C);

  // generic class
  FixedSizeCollection<bool>gA = new FixedSizeCollection<bool>(5); 
  Console.WriteLine(gA);
  FixedSizeCollection<int> gB = new FixedSizeCollection<int>(5); 
  Console.WriteLine(gB);
  FixedSizeCollection<string>gC = new FixedSizeCollection<string>(5);
  Console.WriteLine(gC);
  FixedSizeCollection<string>gD = new FixedSizeCollection<string>(5);
  Console.WriteLine(gD);

The output from the preceding code shows this:

  There are 1 instances of CSharpRecipes.Generics+FixedSizeCollection and this ins
  tance contains 0 items...
  There are 2 instances of CSharpRecipes.Generics+FixedSizeCollection and this ins
  tance contains 0 items...
  There are 3 instances of CSharpRecipes.Generics+FixedSizeCollection and this ins
  tance contains 0 items...
  There are 1 instances of CSharpRecipes.Generics+FixedSizeCollection`1[System.Boo
  lean] and this instance contains 0 items...
  There are 1 instances of CSharpRecipes.Generics+FixedSizeCollection`1[System.Int
  32] and this instance contains 0 items...
  There are 1 instances of CSharpRecipes.Generics+FixedSizeCollection`1[System.Str
  ing] and this instance contains 0 items...
  There are 2 instances of CSharpRecipes.Generics+FixedSizeCollection`1[System.Str
  ing] and this instance contains 0 items...

Discussion

The type parameters in generics allow you to create type-safe code without knowing the final type you will be working with. In many instances, you want the types to have certain characteristics, in which case you place constraints on the type (see Recipe 4.11). Methods can have generic type parameters whether the class itself does or does not.

Notice that whileFixedSizeCollectionhas three instances,FixedSizeCollection, has one instance in which it was declared withboolas the type, one instance in whichintwas the type, and two instances in whichstringwas the declaring type. This means that, while there is one .NETTypeobject created for each nongeneric class, there is one .NETTypeobject for every constructed type of a generic class.

FixedSizeCollection has three instances in the example code becauseFixedSizeCollectionhas only one type that is maintained by the CLR. With generics, one type is maintained for each combination of the class template and the type arguments passed when constructing a type instance. To make it clearer, you get one .NET type forFixedSizeCollection<bool>, one .NET type forFixedSizeCollection<int>, and a third .NET type forFixedSizeCollection<string>.

The staticInstanceCountproperty helps to illustrate this point, as static properties of a class are actually connected to the type that the CLR hangs on to. The CLR creates any given type only once and then maintains it until theAppDomainunloads. This is why the output from the calls toToString()on these objects shows that the count is three forFixedSizeCollection(as there is truly only one of these) and between one and two for theFixedSizeCollection<T>types.

See Also

The “Generic Type Parameters” and “Generic Classes” topics in the MSDN documentation.

4.3 Replacing the ArrayList with Its Generic Counterpart

Problem

You want to enhance the performance of your application as well as make the code easier to work with by replacing all ArrayList objects with the generic version. This is imperative when you find that structures or other value types are being stored in these data structures, resulting in boxing/unboxing operations.

Solution

Replace all occurrences of the System.Collection.ArrayList class with the more efficient generic System.Collections.Generic.List class.

Here is a simple example of using aSystem.Collections.ArrayListobject:

  public static void UseNonGenericArrayList()
 
{
    
// Create and populate an ArrayList.
    
ArrayList numbers = new ArrayList();
    
numbers.Add(1); // Causes a boxing operation to occur
    
numbers.Add(2); // Causes a boxing operation to occur

     // Display all integers in the ArrayList.
     // Causes an unboxing operation to occur on each iteration
     foreach (int i in numbers)
     {
       
Console.WriteLine(i);
     }

     numbers.Clear();
  }

Here is that same code using aSystem.Collections.Generic.Listobject:

  public static void UseGenericList()
 
{
     // Create and populate a List.
     List<int> numbers = new List<int>();
     numbers.Add(1);
     numbers.Add(2);

     // Display all integers in the ArrayList.
     foreach (int i in numbers)
     {
       
Console.WriteLine(i);
     }

     numbers.Clear();
  }

Discussion

Since ArrayLists are used in almost all applications, it is a good place to start to enhance the performance of your application. For simple implementations of the ArrayList in your application, this substitution should be quite easy.

Table 4-1 shows the equivalent members that are implemented in both classes.

Table 4-1. Equivalent members in the ArrayList and the generic List classes

Members in the ArrayList class

Equivalent members in the generic List class

Capacityproperty

Capacityproperty

Countproperty

Countproperty

IsFixedSizeproperty

((IList)myList).IsFixedSize

IsReadOnlyproperty

((IList)myList).IsReadOnly

IsSynchronizedproperty

((IList)myList).IsSynchronized

Itemproperty

Itemproperty

SyncRootproperty

((IList)myList).SyncRoot

Adapter staticmethod

N/A

Addmethod

Addmethod

AddRangemethod

AddRangemethod

N/A

AsReadOnlymethod

BinarySearchmethod

BinarySearchmethod

Clearmethod

Clearmethod

Clonemethod

GetRange(0, numbers.Count)

Table 4-1. Equivalent members in the ArrayList and the generic List classes (continued)

Containsmethod

Containsmethod

N/A

ConvertAllmethod

CopyTomethod

CopyTomethod

N/A

Existsmethod

N/A

Findmethod

N/A

FindAllmethod

N/A

FindIndexmethod

N/A

FindLastmethod

N/A

FindLastIndexmethod

N/A

ForEachmethod

FixedSize staticmethod

N/A

GetRangemethod

GetRangemethod

IndexOfmethod

IndexOfmethod

Insertmethod

Insertmethod

InsertRangemethod

InsertRangemethod

LastIndexOfmethod

LastIndexOfmethod

ReadOnlystatic method

AsReadOnlymethod

Removemethod

Removemethod

N/A

RemoveAllmethod

RemoveAtmethod

RemoveAtmethod

RemoveRangemethod

RemoveRangemethod

Repeatstatic method

Use a forloop and the Addmethod

Reversemethod

Reversemethod

SetRangemethod

InsertRangemethod

Sortmethod

Sortmethod

Synchronizedstatic method

lock(myList.SyncRoot){…}

ToArraymethod

ToArraymethod

N/A

TrimExcessmethod

TrimToSizemethod

TrimToSizemethod

N/A

TrueForAllmethod

In several cases within Table 4-1, there is not a one-to-one correlation between the members of an ArrayList and the members of the generic List class. Starting with the properties, notice that only the Capacity,Count, andItemproperties are present in both classes. To make up for the missing properties in theListclass, you can perform a cast to anIList. The following code shows how to use these casts to get at the missing properties:

  List<int> numbers = new List<int>();

  Console.WriteLine(((IList)numbers).IsReadOnly);
  Console.WriteLine(((IList)numbers).IsFixedSize);
  Console.WriteLine(((IList)numbers).IsSynchronized);
  Console.WriteLine(((IList)numbers).SyncRoot);

Note that due to the absence of code that returns a synchronized version of a genericListand the absence of code that returns a fixed-size genericList, theIsFixedSizeandIsSynchronizedproperties will always returnfalse. TheSyncRoot property will always return the same object on which it is called. Essentially, this property returns thethispointer. Microsoft has decided to remove the ability to create a synchronous wrapper from any of the generic collection classes. Instead, they recommend using thelockkeyword to lock the entire collection or another type of synchronization object that suits your needs.

TheArrayList has several static methods to which there is no direct equivalent method in the genericListclass. To fix this, you have to do a little work. The closest match for the staticArrayList.ReadOnlymethod is theAsReadOnlyinstance method of the genericList class. This makes for a fairly simple substitution.

The staticArrayList.Repeatmethod has no direct equivalent in the genericListclass. So instead, you can use the following generic extension method:

  public static void Repeat<T>(this List<T>list, T obj, int count)
 
{
    
if (count < 0)
    
{
       
throw (new ArgumentException(
                "The count parameter must be greater or equal to zero."));
     }

     for (int index = 0; index < count; index++)
     {
        list.Add(obj);
     }
  }

This generic extension method has three parameters:

list
  
Marks this method as an extension method forList<T>.

obj
  
The object that will be added to the genericList
   object a specified number of times.

count
  
The number of times to add the object contained in
   objto the genericListobject.

Since theClonemethod is also missing from the genericListclass (due to the fact that this class does not implement theICloneableinterface), you can instead use theGetRangemethod of the genericListclass:

  List<int> oldList = new List<int>();
 
// Populate oldList...

  List<int> newList = oldList.GetRange(0, oldList.Count);

TheGetRangemethod performs a shallow copy (similar to theClone method of theArrayList) of a range of elements in theListobject. In this case, the range of elements includes all elements.

See Also

The “System.Collections.ArrayList Class” and “System.Collections.Generic.List Class” topics in the MSDN documentation.

4.4 Replacing the Stack and Queue with Their Generic Counterparts

 

Problem

You want to enhance the performance of your application as well as make the code easier to work with by replacing all Stack and Queue objects with their generic versions. This is imperative when you find that structures or other value types are being stored in these data structures, resulting in boxing/unboxing operations.

Solution

Replace all occurrences of the System.Collections.Stack and System.Collection.Queue objects with the System.Collections.Generic.StackandSystem.Collection.Generic.Queueobjects.

Here is a simple example of using aSystem.Collections.Queueobject:

  public static void UseNonGenericQueue()
 
{
    
// Create a non-generic Queue object.
    
Queue numericQueue = new Queue();

     // Populate Queue (causing a boxing operation to occur).
     numericQueue.Enqueue(1);
     numericQueue.Enqueue(2);
     numericQueue.Enqueue(3);

     // De-populate Queue and display items (causing an unboxing operation to occur)
     Console.WriteLine(numericQueue.Dequeue());
    
Console.WriteLine(numericQueue.Dequeue());
     Console.WriteLine(numericQueue.Dequeue().ToString());
  }

Here is that same code using aSystem.Collections.Generic.Queueobject:

  public static void UseGenericQueue()
 
{
     // Create a generic Queue object.
     Queue<int> numericQueue = new Queue<int>();

     // Populate Queue.
     numericQueue.Enqueue(1);
     numericQueue.Enqueue(2);
     numericQueue.Enqueue(3);

     // De-populate Queue and display items.
     Console.WriteLine(numericQueue.Dequeue());
     Console.WriteLine(numericQueue.Dequeue());
     Console.WriteLine(numericQueue.Dequeue());
 
}

Here is a simple example of using aSystem.Collections.Stackobject:

  public static void UseNonGenericStack()
  {
     // Create a non-generic Stack object.
     Stack numericStack = new Stack();

     // Populate Stack (causing a boxing operation to occur).
     numericStack.Push(1);
     numericStack.Push(2);
     numericStack.Push(3);

     // De-populate Stack and display items (causing an unboxing operation to
occur).
     Console.WriteLine(numericStack.Pop().ToString());
     Console.WriteLine(numericStack.Pop().ToString());
     Console.WriteLine(numericStack.Pop().ToString());
  }

Here is that same code using aSystem.Collections.Generic.Stackobject:

  public static void UseGenericStack()
 
{
     // Create a generic Stack object.
     Stack<int> numericStack = new Stack<int>();

     // Populate Stack.
     numericStack.Push(1);
     numericStack.Push(2);
     numericStack.Push(3);

     // De-populate Stack and display items.
     Console.WriteLine(numericStack.Pop().ToString());
     Console.WriteLine(numericStack.Pop().ToString());
     Console.WriteLine(numericStack.Pop().ToString());
  }

Discussion

On the surface, the generic and nongeneric Queue and Stack classes seem similar enough. However, it is a very different story underneath the surface. The basic use of the generic Queue andStackobjects are the same as with their nongeneric counterparts, except for the syntax used to instantiate the objects. The generic form requires a type argument in order to create the type. The type argument in this example is anint. This type argument indicates that thisQueueorStackobject will be able to contain only integer types, as well as any type that implicitly converts to an integer, such as ashort:

  short s = 300;
 
numericQueue.Enqueue(s);   // OK, because of the implicit conversion 

However, a type that cannot be implicitly converted to an integer, such as adouble, will cause a compile-time error:

  double d = 300;
  numericQueue.Enqueue(d);        // Error, no implicit conversion available
  numericQueue.Enqueue((int)d);   // OK, because of the explicit cast

The nongeneric form does not require this type argument, because the nongenericQueueandStack objects are allowed to contain any item as an element because all items are convertible to typeObject.

When choosing between a generic and nongenericQueueorStack, you need to decide whether you wish to use a genericQueue orStackobject or a nongenericQueueorStack object. Choosing the genericQueueorStackclass over its nongeneric form gives you many benefits, including:

Type-safety

Each element contained in the data structure is typed to one specific type. This means no more casting of objects when they are added to or removed from the data structure. You cannot store multiple disparate types within a single data structure; you always know what type is stored within the data structure. Type checking is done at compile time rather than runtime. This boils down to writing less code, achieving better performance, and making fewer errors.

Shortened development time

To make a type-safe data structure without using generics means having to subclass theSystem.Collections.QueueorSystem.Collections.Stackclass in order to create your own. This is time-consuming and error-prone.

Performance

The genericQueue orStackdoes not require a cast that could fail to occur when adding and removing elements from it. In addition, no boxing operation occurs when adding a value type to theQueueorStack. Likewise, in almost all cases, no unboxing operation occurs when removing a value type from theQueueorStack.

Easier-to-read code

Your code base will be much smaller because you will not have to subclass the nongenericQueueorStackclass to create your own strongly typed class. In addition, the type-safety features of generic code will allow you to better understand what the purpose of theQueue orStackclass is in your code.

The following class members are implemented in the nongenericQueueandStack classes but not in their generic counterparts:

  Clonemethod
  IsSynchronizedproperty
 SyncRootproperty
 Synchronizedmethod

The addition of theClonemethod on the nongenericQueueandStackclasses is due to theICloneable interface being implemented only on the nongenericQueueandStackclasses. However, all other interfaces implemented by the generic and nongenericQueue andStack classes are identical.

One way around the missingClonemethod in the genericQueue andStackclasses is to use the constructor that accepts anIEnumerable<T>type. Since this is one of the interfaces that theQueueandStackclasses implement, it is easy to write. For theQueueobject, the code is as follows:

  public static void CloneQueue()
 
{
    
// Create a generic Queue object.
    
Queue<int> numericQueue = new Queue<int>();

     // Populate Queue.
    
numericQueue.Enqueue(1);
    
numericQueue.Enqueue(2);
    
numericQueue.Enqueue(3);

     // Create a clone of the numericQueue.
     Queue<int> clonedNumericQueue = new Queue<int>(numericQueue);

     // This does a simple peek at the values, not a dequeue.
     foreach (int i in clonedNumericQueue)
     {
        Console.WriteLine("foreach: " + i.ToString());
     }

     // De-populate Queue and display items.
    
Console.WriteLine(clonedNumericQueue.Dequeue().ToString());
    
Console.WriteLine(clonedNumericQueue.Dequeue().ToString());
    
Console.WriteLine(clonedNumericQueue.Dequeue().ToString());
  }

The output for this method is shown here:

  foreach: 1
  foreach: 2
  foreach: 3
  1
  2
  3

For theStackobject, the code is as follows:

  public static void CloneStack()
 
{
     // Create a generic Stack object.
     Stack<int> numericStack = new Stack<int>();

     // Populate Stack.
     numericStack.Push(1);
     numericStack.Push(2);
     numericStack.Push(3);

     // Clone the numericStack object.
     Stack<int> clonedNumericStack = new Stack<int>(numericStack);

     // This does a simple peek at the values, not a pop.
     foreach (int i in clonedNumericStack)
     {
       
Console.WriteLine("foreach: " + i.ToString());
     }

     // De-populate Stack and display items.
     Console.WriteLine(clonedNumericStack.Pop().ToString());
     Console.WriteLine(clonedNumericStack.Pop().ToString()); 
     Console.WriteLine(clonedNumericStack.Pop().ToString());
 
}

The output for this method is shown here:

  foreach: 1
  foreach: 2
  foreach: 3
  1
  2
  3

This constructor creates a new instance of theQueue orStackclass containing the elements copied from theIEnumerable<T>type.

See Also

The “System.Collections.Stack Class,” “System.Collections.Generic.Stack Class,” “System.Collections.Queue Class,” and “System.Collections.Generic.Queue Class” topics in the MSDN documentation.

Please check back next week for the continuation of this article.

blog comments powered by Disqus
C# ARTICLES

- Beginning C#
- ASP.NET RedirectPermanent Method using C# an...
- C Programming Language and UNIX Pioneer Pass...
- Using Facebook JavaScript SDK in ASP.NET wit...
- ASP.NET Export to Excel and Word using VB.NE...
- WAV and MP3 Streaming with ASP.Net and C#
- Game Programming using SDL: the File I/O API
- C# and Java Developer Jobs on the Rise
- The Future Evolution of C# and VB.NET
- C# If and Else-if Statements
- How To Use the C# String Replace Method
- 5 Ways to Parse XML in C#
- C# Meets Design Patterns
- Coding a CRC-Generating Algorithm in C
- Cyclic Redundancy Check

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
 
 
 

ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 3 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials