C#
  Home arrow C# arrow Page 2 - Generics
ASP Free Forums 
.NET  
ASP  
ASP Code  
ASP.NET  
ASP.NET Code  
BrainDump  
C#  
Code Examples  
Database  
Database Code  
IIS  
Microsoft Access  
MS SQL Server  
Silverlight  
Visual Basic.NET  
Windows Scripting  
Windows Security  
XML  
Mobile Linux 
App Generation ROI 
IBM® developerWorks 
ASP Web Hosting  
ASP.NET Web Hosting 
Windows Web Hosting
 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
C#

Generics
By: O'Reilly Media
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 5 stars5 stars5 stars5 stars5 stars / 2
    2008-12-11

    Table of Contents:
  • Generics
  • 4.2 Understanding Generic Types
  • 4.3 Replacing the ArrayList with Its Generic Counterpart
  • 4.4 Replacing the Stack and Queue with Their Generic Counterparts

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT


    Generics - 4.2 Understanding Generic Types


    (Page 2 of 4 )

    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.

    More C# Articles
    More By O'Reilly Media


       · This article is an excerpt from the book "C# 3.0 Cookbook, Third Edition," published...
     

    Buy this book now. This article 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). Check it out today at your favorite bookstore. Buy this book now.

    C# ARTICLES

    - C# Meets Design Patterns
    - Coding a CRC-Generating Algorithm in C
    - Cyclic Redundancy Check
    - Handling Methods and Functions
    - Destroying Objects in C#
    - Creating Objects in C-Sharp
    - Classes and Objects
    - Programming Languages: Managed versus Native
    - LINQ-to-MySQL with DbLinq in C#
    - Working with Dates and Times in C#
    - Generics, Dictionaries, and More
    - More About Generics
    - Working with C# Collections
    - Generics
    - C# and XML





    © 2003-2010 by Developer Shed. All rights reserved. DS Cluster 9 Hosted by Hostway
    For more Enterprise Application Development news, visit eWeek