9.4. Collection InterfacesThe .NET Framework provides two sets of standard interfaces for enumerating and comparing collections: the traditional (nontype-safe) and the new generic type-safe collections. This book focuses only on the new, type-safe collection interfaces as these are far preferable. You can declare an ICollection of any specific type by substituting the actual type (for example, int or string) for the generic type in the interface declaration (<T>).
The key generic collection interfaces are listed in Table 9-2.[3]
9.4.1. The IEnumerable<T> InterfaceYou can support the foreach statement in ListBoxTest by implementing the IEnumerable<T> interface (see Example 9-11). IEnumerable has only one method, GetEnumerator( ), whose job is to return an implementation of IEnumerator<T>. The C# language provides special help in creating the enumerator, using the new keyword yield. Example 9-11. Making a ListBox an enumerable class#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace Enumerable { public class ListBoxTest : IEnumerable<String> { private string[] strings; private int ctr = 0; // Enumerable classes can return an enumerator public IEnumerator<string> GetEnumerator( ) { foreach ( string s in strings ) { yield return s; } } // initialize the list box with strings public ListBoxTest( params string[] initialStrings ) { // allocate space for the strings strings = new String[8]; // copy the strings passed in to the constructor foreach ( string s in initialStrings ) { strings[ctr++] = s; } } // add a single string to the end of the list box public void Add( string theString ) { strings[ctr] = theString; ctr++; } // allow array-like access public string this[int index] { get { if ( index < 0 || index >= strings.Length ) { // handle bad index } return strings[index]; } set { strings[index] = value; } } // publish how many strings you hold public int GetNumEntries( ) { return ctr; } } public class Tester { static void Main( ) { // create a new list box and initialize ListBoxTest lbt = new ListBoxTest( "Hello", "World" ); // add a few strings lbt.Add( "Who" ); lbt.Add( "Is" ); lbt.Add( "John" ); lbt.Add( "Galt" ); // test the access string subst = "Universe"; lbt[1] = subst; // access all the strings foreach ( string s in lbt ) { Console.WriteLine( "Value: {0}", s ); } } } } Output: Value: Hello Value: Universe Value: Who Value: Is Value: John Value: Galt Value: Value: The program begins in Main( ), creating a new ListBoxTest object and passing two strings to the constructor. When the object is created, an array of Strings is created with enough room for eight strings. Four more strings are added using the Add method, and the second string is updated, just as in the previous example. The big change in this version of the program is that a foreach loop is called, retrieving each string in the listbox. The foreach loop automatically uses the IEnumerable<T> interface, invoking GetEnumerator( ). The GetEnumerator method is declared to return an IEnumerator of string: public IEnumerator<string> GetEnumerator( ) The implementation iterates through the array of strings, yielding each in turn: foreach ( string s in strings ) { yield return s; } All the bookkeeping for keeping track of which element is next, resetting the iterator, and so forth, is provided for you by the framework. |