Recipe 9.15 Creating a Strongly Typed Collection

Problem

You have some particular data type (and its descendent types) that you wish to store in a collection, and you do not want users of your collection to store any other data types within it.

Solution

Create a strongly typed collection by inheriting from the CollectionBase abstract base class. There are two ways to create a strongly typed collection; the first is to modify the parameters for all the overloaded methods to accept only a particular type. For example, instead of the Add method accepting a generic Object data type, you can change it to accept only one particular data type. A collection base that accepts only objects of a particular type ( Media ) or its descendents ( Magnetic , Optical , or PunchCard ) is shown here (note that the Media class and its descendents are defined in Recipe 3.4):

 public class MediaCollection : CollectionBase {     public MediaCollection( ) : base( )     {     }     public Media this[int index]       {         get           {             return ((Media)List[index]);         }         set           {             List[index] = value;         }     }     public int Add(Media item)       {         return (List.Add(item));     }     public int IndexOf(Media item)       {         return(List.IndexOf(item));     }     public void Insert(int index, Media item)       {         List.Insert(index, item);     }     public void Remove(Media item)       {         List.Remove(item);     }     public bool Contains(Media item)     {         return(List.Contains(item));     } } 

The next method of writing a strongly typed collection involves the OnValidate event. This event is fired immediately before any action that modifies the data within the collection. The next strongly typed collection operates the same as the previous MediaCollection class, except that it uses an event to make sure that only a particular type and/or its descendents are operated on:

 public class MediaCollectionEv : CollectionBase {     public MediaCollectionEv( ) : base( )     {     }     public object this[int index]       {         get           {             return (List[index]);         }         set           {             List[index] = value;         }     }     public int Add(object item)       {         return (List.Add(item));     }     public int IndexOf(object item)       {         return(List.IndexOf(item));     }     public void Insert(int index, object item)       {         List.Insert(index, item);     }     public void Remove(object item)       {         List.Remove(item);     }     public bool Contains(object item)     {         return(List.Contains(item));     }     protected override void OnValidate(object item)       {         if (!(item is Media))         {             throw new ArgumentException("This collection only accepts " +               "the Media type or types that derive from Media");         }     } } 

Discussion

Most of the collection types built in to the FCL are generic; that is, they accept only the most basic typethe Object type. Sometimes it is good to have a more specialized collection type (usually referred to as strongly typed collections ) that can contain only objects of one particular type. Of course, this collection would also be able to contain objects of types descending from this one particular type.

There are several benefits to writing a strongly typed collection, such as reducing the number of potential errors that can be coded into your application. If you are only expecting a particular type to be contained within a collection, and a piece of code inadvertently adds objects not of this type, your code might fail when attempting to operate on this unexpected type. If the first of the two strongly typed collections were used, the compiler would catch this mistake earlier in the development phase. Note that the OnValidate event will work only at runtime.

Another useful side effect of using the first of the two strongly typed collections is that you do not have to cast the object being returned from the collection to its correct type before using it. A strongly typed collection automatically returns the type you expect, as opposed to an Object type, which must then be cast to the expected type.

A benefit of either strongly typed collection is that you can add specialized code to your collection to more easily allow you to operate on the objects contained in your collection. For example, if you wrote a strongly typed collection to contain only Invoice type objects, you could add methods to this collection to do the following:

  • Retrieve only those invoices that match a specific criteria, such as being overdue.

  • Reject attempts to add invoices to this collection that do not meet a criterion, such as a minimum amount.

  • Prevent invoice objects from being removed by throwing a NotImplementedException when a Remove method is called and overloading the RemoveAt method to do the same, so that invoices cannot be removed.

Now that you have a reason for building a strongly typed collection, you have three choices for doing so:

  1. Inherit from the CollectionBase abstract base class and implement the members so that they operate on a specific type, other than Object , as in the MediaCollection class defined in the Solution section.

  2. Inherit from CollectionBase and override the OnValidate event, as with the MediaCollectionEv class defined in the Solution section.

  3. Build your own from scratch (this technique is not covered in this recipe since the previous two ways are much easier to implement).

Many developers opt for the first technique, which involves adding methods to the collection, such as Add , Remove , IndexOf , and others that operate on a specific type. This technique best aids the developer for two important reasons. First, the developer can examine the exact type that this collection is expecting by using the Intellisense features of the IDE. Second, the developer is alerted at compile time when the collection is not being used as it should, via compile-time errors.

The second technique is very similar to the first technique, but instead of writing strongly typed methods such as Add , Remove , and so on, these methods are written to accept and return the Object type. Instead of preventing other data types from being contained in this collection, an event handler, called OnValidate , is added to validate the object being added to, removed from, and so on, with regard to the collection. If the object is of the correct type, the event handler does nothing, allowing the collection to perform the specified action. If the object is not of the correct type, an exception is thrown, preventing the collection from performing the specified action.

Note that when using weakly typed methods with the OnValidate event handler, the compiler will not be able to validate any use of the strongly typed collection. However, this event handler is useful when you want to consolidate all validation routines for your collection. In fact, there is no reason why the first and second techniques cannot be combined (i.e., strongly typed methods along with the OnValidate event handler for further validations).

See Also

See Recipe 3.4; see the "CollectionBase Class" and "Creating and Manipulating Collections" topics in the MSDN documentation.



C# Cookbook
C# 3.0 Cookbook
ISBN: 059651610X
EAN: 2147483647
Year: 2003
Pages: 315

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