4.13 Creating New Collection Types

 <  Day Day Up  >  

None of the .NET Framework collection classes will work for the type of data you want to store. You want to create your own collection class.


Technique

Determine which collection class is similar to what you want to accomplish and create a derived class from that collection class. Overload any item-manipulation methods necessary for your new collection. You might also choose to implement a collection that isn't based on a predefined collection, or you might want to create a strongly typed collection. In either case, for a list-based collection, create a class derived from CollectionBase , and for a dictionary-based collection, create a class derived from DictionaryBase .

Comments

Due to the design of the collection classes within the .NET Framework, implementing a collection class of your own is much simpler because you can borrow implementation details from the built-in collection classes. Depending on your needs, there are a couple of ways to go about implementing your own collection.

If you want to alter the behavior of a collection but keep a majority of the implementation details intact, you can use inheritance to your advantage. This process saves you from a lot of the bookkeeping tasks associated with collection classes while still giving you control of how objects are manipulated within your collection. Let's take the example from the previous recipe, which worked with a list of first and last names . You can create a class that derives from ArrayList and overload some of the methods to validate the values that are added to the collection. For instance, by overloading the Add method, you can ensure that the object passed in is a string object and that it contains a first name and a last name separated by a space:

 
 class NameCollection : ArrayList {     public override int Add( object value )     {         if( value.GetType() == Type.GetType("System.String" ))         {             string[] name = ((string)value).Split( new char[]{' '});             if( name.Length == 2 )                 return base.Add( value );         }         return -1;     }     public int Add( string value )     {         string[] name = ((string)value).Split( new char[]{' '});         if( name.Length == 2 )             return base.Add( value );         return -1;     } } 

Deriving from a .NET collection class can be a real time-saver and allows you to slightly alter the behavior of a collection class. However, you might need an even finer grain of control over your collection. The NameCollection example would benefit more if you created a strongly typed collection. A strongly typed collection is one that uses a certain data type for its values rather than generic object instances.

To create a strongly typed collection, derive from either CollectionBase if your collection is list based or DictionaryBase if your collection needs to be dictionary based like a hash table. These classes implement the appropriate interfaces for a .NET collection. Furthermore, they also provide protected methods and properties that you can use to add objects to the collection.

We'll take a look at an example of creating a strongly typed list collection that represents a hand of cards. The definition of a Card is similar to the example shown in the sorting recipe, which means it contains fields for data and properties to access those fields and implements the IComparable interface to compare two cards. The comparison checks the suit of a card first, and if they are equal, it compares the actual value of the card. Listing 4.7 shows the implementation of the Card class.

Listing 4.7 Card Type Containing a Card Value and Suit Defined with Enumerated Types
 public enum CardSuits {     Hearts = 1, Clubs, Diamonds, Spades } public enum CardValues {     Two=2, Three, Four, Five, Six, Seven, Eight, Nine, Ten,     Jack = 11, Queen, King, Ace } public class Card : IComparer {     int number;     CardSuits suit;     public Card() : this( CardValues.Two, CardSuits.Hearts )     {     }     public Card( CardValues cvalue, CardSuits csuit )     {         // ensure values fall within range         if( (int) cvalue > 1 && (int)cvalue < 15 )             number = (int) cvalue;         else             number = 2;         // ensure suits fall within range         if( (int) csuit > 0 && (int) csuit < 5 )             suit = csuit;         else             suit = CardSuits.Hearts;     }     public override string ToString()     {         string ret;         // get face card name if card is greater than 10         if( number > 10 )         {             ret = Enum.GetName( typeof(CardValues), number );         }         else         {             ret = number.ToString();         }         ret += " of ";         // get suit name from CardSuits enum         ret += Enum.GetName( typeof(CardSuits), suit );         return ret;     }     public CardSuits CardSuit     {         get         {             return suit;         }         set         {             // make sure suit falls within range             if( (int) value > 0 && (int) value < 5 )                 suit = value;         }     }     public CardValues CardValue     {         get         {             return (CardValues) number;         }         set         {             // make sure value is within range             if( (int) value > 1 && (int) value < 15 )             {                 number = (int) value;             }         }     }     public int Compare(object x, object y)     {         Card a = (Card) x;         Card b = (Card) y;         // check card suits         if( (int) a.CardSuit < (int) b.CardSuit )             return -1;         if( (int) a.CardSuit > (int) b.CardSuit )             return 1;         // check card values since suits are the same         if( (int) a.CardValue < (int) b.CardValue )             return -1;         if( (int) a.CardValue > (int) b.CardValue )             return 1;         return 0;     } } 

If you were to create a class that simply derived from ArrayList , a person using the class might add a different object, an integer for example, to the collection. This class obviously doesn't work for what you want to do. Listing 4.8 shows the CardHand class. This class is a strongly typed collection class derived from CollectionBase and only allows you to place Card objects into the collection. The reason it works is that the CollectionBase class uses explicit interface implementation to "hide" the interface methods of the collection interfaces. It allows you to restrict the types of objects added to the collection. However, you can still cast your collection object to one of the collection interfaces, thereby allowing you to add an object not designed to work with your collection. To circumvent this, the CollectionBase class contains several events that you can handle to prevent the addition of data types not consistent with your collection. (Events are covered in Chapter 5, "Delegates and Events.")

During implementation of your collection, because the CollectionBase base class contains a protected ArrayList member variable, you are free to use that as the internal collection and route any method calls on your object to that internal collection, a process known as containment . Figure 4.7 shows the result of running the application in Listing 4.8.

Listing 4.8 Implementing a Strongly Typed Collection by Deriving from CollectionBase and Storing Card Objects
 using System; using System.Collections; namespace _13_CardCollection {     //     // Card implementation from Listing 4.7 here     //     public class CardHand : CollectionBase     {         public CardHand()         {         }         public override string ToString()         {             string ret = "";             // print out name of each card in hand             foreach( Card c in base.InnerList )                 ret += c.ToString() + "\n";             return ret;         }         public void GenerateRandomHand( int numCards )         {             // initialize random number generator using tick count             Random rand = new Random( (int)DateTime.Now.Ticks );             // copy to array and sort             ArrayList sortedCards = (ArrayList) base.InnerList.Clone();             sortedCards.Sort( new Card() );             for( int i = 0; i < numCards; i++ )             {                 Card newCard;                 do                 {                     int val = rand.Next( 2, 15 );                     int suit = rand.Next( 1, 4 );                     // create new random card                     newCard = new Card( (CardValues) val, (CardSuits) suit );                    // make sure card doesn't already exist                    // by searching current hand                 } while( sortedCards.BinarySearch(newCard, newCard ) > 0);                 // add to sorted cards array                 sortedCards.Add( newCard );                 Add( newCard );                 // resort the sortedCards collection                 sortedCards.Sort( new Card() );             }         }         public int Add( Card value )         {              // add to base class list              return base.InnerList.Add( (object)value );         }         public void Remove( Card value )         {             // remove from base class list             base.InnerList.Remove( value );         }         public void Sort()         {             base.InnerList.Sort( new Card() );         }     }     class Class1     {         [STAThread]         static void Main(string[] args)         {             CardHand hand = new CardHand();             hand.GenerateRandomHand( 5 );             Console.WriteLine( "Random Hand\n" + hand.ToString() );             hand.Sort();             Console.WriteLine( "\nSorted Hand\n" + hand.ToString() );             Console.WriteLine( "Total of {0} cards", hand.Count );         }     } } 
Figure 4.7. The CardHand collection works with Card objects to simulate a collection of cards.

graphics/04fig07.gif

It is important to note that if your collection is not list or dictionary based, the method of creating the collection is still similar. Rather than derive from a class such as CollectionBase or DictionaryBase , you instead implement a number of interfaces and their associated methods. Generally, you implement ICollection and, if you allow enumeration, the IEnumerable interface. Even if you decide to store the objects differently within your class ”using a red-black tree, for example ”you might still want to implement the same method signatures shown in other collections. List-based collection classes implement the IList interface, and dictionary-based collections implement the IDictionary interface.

 <  Day Day Up  >  


Microsoft Visual C# .Net 2003
Microsoft Visual C *. NET 2003 development skills Daquan
ISBN: 7508427505
EAN: 2147483647
Year: 2003
Pages: 440

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