C#
  Home arrow C# arrow Page 3 - Generics, Dictionaries, and More
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, Dictionaries, and More
By: O'Reilly Media
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 4 stars4 stars4 stars4 stars4 stars / 3
    2008-12-24

    Table of Contents:
  • Generics, Dictionaries, and More
  • 4.10 Using foreach with Generic Dictionary Types
  • 4.11 Constraining Type Arguments
  • 4.12 Initializing Generic Variables to Their Default Values

  • 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, Dictionaries, and More - 4.11 Constraining Type Arguments


    (Page 3 of 4 )

    Problem

    Your generic type needs to be created with a type argument that must support the members of a particular interface such as the IDisposable interface.

    Solution

    Use constraints to force the type arguments of a generic type to be of a type that implements one or more particular interfaces:

      public class DisposableList<T>: IList<T>
         where T : class, IDisposable
      {
         private List<T>_items = new List<T>();

         // Private method that will dispose of items in the list
         private void Delete(T item)
         {
           
    item.Dispose();
         }

         // IList<T>Members
        
    public int IndexOf(T item)
        
    {
           
    return (_items.IndexOf(item));
         }

         public void Insert(int index, T item)
         {
            _items.Insert(index, item);
         }

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

         public void RemoveAt(int index)
         {
           
    Delete(this[index]);
            _items.RemoveAt(index);
         }

         // ICollection<T>Members
         public void Add(T item)
         {
           
    _items.Add(item);
         }

         public bool Contains(T item)
         {
            return (_items.Contains(item));
         }

         public void CopyTo(T[] array, int arrayIndex)
         {
            _items.CopyTo(array, arrayIndex);
         }

         public int Count
         {
            get     {return (_items.Count);}
         }

         public bool IsReadOnly
         {
            get     {return (false);}
         }

         // IEnumerable<T>Members
         public IEnumerator<T>GetEnumerator()
         {
           
    return (_items.GetEnumerator());
         }

         // IEnumerable Members
         IEnumerator IEnumerable.GetEnumerator()
         {
           
    return (_items.GetEnumerator());
         }

         // Other members
         public void Clear()
         {
           
    for (int index = 0; index < _items.Count; index++)
            {
               Delete(_items[index]);
            }

            _items.Clear();
         }

         public bool Remove(T item)

         {
            int index = _items.IndexOf(item);

            if (index >= 0)
           
    {
               Delete(_items[index]);
               _items.RemoveAt(index);

               return (true);
            }
            else
            {
               return (false);
            }
         }
      }

    ThisDisposableListclass allows only an object that implementsIDisposableto be passed in as a type argument to this class. The reason for this is that whenever an object is removed from aDisposableList object, theDisposemethod is always called on that object. This allows you to transparently handle the management of any object stored within thisDisposableListobject.

    The following code exercises aDisposableList object:

      public static void TestDisposableListCls()
      {
         DisposableList<StreamReader>dl = new DisposableList<StreamReader>();

         // Create a few test objects.
         StreamReader tr1 = new StreamReader("c:\\boot.ini");
         StreamReader tr2 = new StreamReader("c:\\autoexec.bat");
         StreamReader tr3 = new StreamReader("c:\\config.sys");

         // Add the test object to the DisposableList.
         dl.Add(tr1);
         dl.Insert(0, tr2);
         dl.Add(tr3);

         foreach(StreamReader sr in dl)
         {
            Console.WriteLine("sr.ReadLine() == " + sr.ReadLine());
         }

         // Call Dispose before any of the disposable objects are
         // removed from the DisposableList.
         dl.RemoveAt(0);
         dl.Remove(tr1);
         dl.Clear();
     
    }

    Discussion

    The where keyword is used to constrain a type parameter to accept only arguments that satisfy the given constraint. For example, the DisposableList has the constraint that any type argument T must implement the IDisposable interface:

      public class DisposableList<T>: IList<T>
             where T : IDisposable

    This means that the following code will compile successfully:

      DisposableList<StreamReader>dl = new DisposableList<StreamReader>();

    but the following code will not:

      DisposableList<string>dl = new DisposableList<string>();

    This is because thestring type does not implement theIDisposableinterface, and theStreamReadertype does.

    Other constraints on the type argument are allowed, in addition to requiring one or more specific interfaces to be implemented. You can force a type argument to be inherited from a specific base class, such as theTextReaderclass:

      public class DisposableList<T>: IList<T>
            where T : System.IO.TextReader, IDisposable

    You can also determine if the type argument is narrowed down to only value types or only reference types. The following class declaration is constrained to using only value types:

      public class DisposableList<T>: IList<T>
             where T : struct

    This class declaration is constrained to only reference types:

      public class DisposableList<T>: IList<T>
             where T : class

    In addition, you can also require any type argument to implement a public default constructor:

      public class DisposableList<T>: IList<T>
            where T
    : IDisposable,
    new()

    Using constraints allows you to write generic types that accept a narrower set of available type arguments. If the IDisposable constraint is omitted in the Solution for this recipe, a compile-time error will occur. This is because not all of the types that can be used as the type argument for the DisposableList class will implement the IDisposable interface. If you skip this compile-time check, a DisposableList object may contain objects that do not have a public no-argument Dispose method. In this case, a runtime exception will occur. Generics and constraints in particular force strict type checking of the class-type arguments and allow you to catch these problems at compile time rather than at runtime.

    See Also

    The “where Keyword” topic 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

    - 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
    - Pointers and Arrays in C#





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