June 5, 2010, 7:08 a.m.
posted by pumi
Storing Data with the System.Collections ClassesThe .NET Framework provides a rich set of classes you can use to store collections or sets of data. This includes the common approaches such as using an array, plus many specialist classes such as ArrayList, HashTable, SortedList, Stack, and Queue. While you can implement these kinds of collections using custom code, the built-in classes make it much easier by automatically handling issues such as increasing the size to accommodate new items, and providing methods for interacting with the contents. In this section, you will see come examples of using these classes. The main collection classes are in the namespace System.Collections, while the System.Collections.Specialized namespace contains some more specialized types such as NameValueCollection and StringCollection. The generic collections classes, which allow you to create collections of specific or custom objects, are in the System.Collections.Generic namespace. You must ensure that you add a reference to the appropriate namespace to your project, and import these namespaces into your pages when you use these collection classes. ArrayListsProbably the most commonly used collection class is the ArrayList, which provides a way to store values of any type in a structure based on an array. It provides properties that indicate the capacity and current size; and a wide range of methods that allow you to resize the list; and add, remove, copy, and find items. For example, the code in Listing 15.6 (taken from the GetCollection method of the Ch15DataProcess.cs file) shows how you can create and add String values to an ArrayList, then sort the list based on the default sorting order of the contents. You can also specify start and end indexes to sort sections of the ArrayList. If you store custom objects in the list, or want to sort it in a non-standard way, you can create a class that implements the IComparer interface and use this when sorting the contents. Creating, Populating, and Sorting an ArrayList
Listing 15.7 shows how you can clear all the items from an ArrayList, and then add items from another collection to the end. In this case, it is a Queue but it could be another ArrayList or other type of collection. To insert items in the middle of the ArrayList, you would use the InsertRange method. This code also shows how you can find and remove items using the Contains method to see if it exists first, and then locating it using the IndexOf method and removing it with the RemoveAt method. There is also the LastIndexOf method for locating the last occurrence of an item, and the Remove method that removes the first instance of a specified item. Notice in the last line of Listing 15.7 how you can convert an ArrayList into a normal array, specifying the data type of the items for the array. Clearing, Adding a Range, Searching, and Removing an Item from an ArrayList
Stacks and QueuesThe GetCollection method in the example application also demonstrates use of a Stack and Queue collection. A Stack provides a "last-in-first-out" (LIFO) collection where you Push an item onto the stack and the Pop it from the top of the stack, or Peek to see what the top item is without removing it. You can also query properties of the Stack class to see how many items it contains, clear the stack with the Clear method, and see if it contains an item using the Contains method. The Queue collection provides a similar set of properties and methods to the Stack, but is a "first-in-first-out" (FIFO) collection. You add items to the end of the queue using the Enqueue method, and remove them from the front of the queue using the Dequeue method. Listing 15.8 shows how the example uses a Stack and a Queue, including creating them from an existing collection. Using the Stack and Queue Classes
Sorted Lists, HashTables, and Dictionary-Based collectionsThe collection classes you have seen so far are one-dimensional, in that they store what is, basically, a list of values. You can, however, create custom classes or objects and store these in a collection such as an ArrayList. Other collection types store values in dictionary form, where a key locates each entry. Examples of this are the SortedList, HashTable, StringDictionary, and NameValueCollection. These types of collections provide access via a key value, and you can obtain a list of the keys or a list of the values from the Keys and Values properties of the collection, or (depending on the type of list) use methods such as GetKey and GetValue to retrieve items. There are also properties and methods such as Clear, Add, Remove, and CopyTo for examining or manipulating the contents of the collections. ASP.NET uses the NameValueCollection class for tasks such as storing the name/value pairs in a query string, and is ideal where you want to be able to manipulate values using the key name. The StringDictionary class is a good way to manage a simple list of String values where a key identifies each one. The HashTable class uses a hash function to generate unique keys that identify values, and stores the values internally in a way that optimizes performance when searching for and retrieving values. The SortedList class, meanwhile, is a more complex class that automatically maintains the contents sorted into the default order of the key values. This makes it ideal for situations where you need the items sorted at all times, and can considerably simplify your code. When you create a SortedList instance, you canas with the ArrayListprovide a custom object that implements the IComparer interface to manage the sort order in a non-standard way. The SortedList class also implements many more methods than the other Dictionary-based classes. For example, there are the ContainsKey, ContainsValue, IndexOfKey, and IndexOfValue methods for locating items, GetByIndex for retrieving items, and RemoveAt for removing an itemover and above the methods implemented by the other collection classes. Listing 15.9 shows some of the ways you can use the SortedList class (this code is also contained in the GetCollection method of the Ch15DataProcess.cs file). It takes an Array of String values and inserts these into a new SortedList instance in the order they appear in the array. You cannot iterate over an Array using a foreach construct, and so the code demonstrates an alternative approach. You could use a for loop, and stop when it reaches the end of the array determined by the Length property. However, this example shows how you can create an enumerator, which works over many list types including the SortedList. A call to the GetEnumerator method returns an instance of a class that implements the IEnumerator interface. This interface contains methods that allow you to iterate over the list, including the MoveNext method that returns false when it reaches the end of the list. At any point, the Current property of the enumerator returns a reference to the current item in the list. The result of the code in Listing 15.9, combined with the previous blocks of code in this section of the chapter, creates a StringBuilder containing a list of animals in the variable sourceString. The example then displays this string in the third View control and makes it the active view, to give the result shown in Figure. You can see the original ordering of items in the ArrayList, Stack, and Queue collections after adding and removing them. However, the SortedList automatically maintains the ordering based on the key valuewhich in this case is the name of the animal. 3. The result of creating a collection of values using several of the collection classes with the Framework
Using the SortedList Class
How the Example Returns ValuesYou can see that the value returned from the GetCollection method is a String, stored in the variable sourceValue. This variable, along with a variable named sourceBytes, is declared at the beginning of the Click event handler for the Start button (btnStart_Click). This event handler manages the selection of the function that generates the initial object, applies the selected process(es) to it, and then generates the type of output selected in the initial page of the example. Depending on which type of item the example is creating, it stores the value either as a String or as a Byte array. A third variable named sourceType indicates which of the other two variables contains the result. The code sets this variable to one of the values from the SourceFileType enumeration that is declared in the Ch15DataProcess.cs file. Each routine in the Ch15DataProcess.cs file that creates a source item takes a reference to both the Byte array and the String, and returns a SourceFileType value to indicate the type of data created. Serializing a CollectionAs an example of returning an array of Bytes, Listing 15.10 shows the complete routine that creates a HashTable, fills it with some values, and then returns the contents in the sourceBytes variable. Because the HashTable class supports serialization (it implements the ISerializable interface), the code can create a serialized representation of the class using a Binary-Formatter instance. A call to the Serialize method of the BinaryFormatter generates a Stream of bytes, which the example captures in a MemoryStream and then converts into an array of Bytes. You will see more about using streams in the section "Working with Streams, StreamReaders, and StreamWriters" later in this chapter. If you select Dictionary of values as the source item type in the example page, and Memory stream as the output type, you will see the results of serializing the HashTable objectas shown in Figure. 4. Serializing a HashTable instance and displaying the serialized contents
The Routine to Create a HashTable Instance as an Array of Bytes
Remember to import the System.Runtime.Serialization and System. Runtime.Serialization.Formatters.Binary namespaces if you want to use the BinaryFormatter class as shown here. Generic CollectionsOne of the recent features added to both the C# and VB.NET languages is support for generics. You can use generics to define data structures that are type-safe, in other words will only accept instances of values and objects of a specific type, without having to actually specify the type when you build the structure. You then use generic syntax to create instances of these types that are specific to the required data type. For example, if you have a class that stores instances of some generic type, and you want to be able to use the class with different data types, you can declare it like this: public class MyStructure<T>
{
... implementation here ...
}You can then create an instance of this structure that works with integer values using: MyStructure<Int32> theStruct = new MyStructure<Int32>(); If you then try and pass any item other than an Int32 to the class, it will raise an exception as it is now specific to it the Int32 data type. The result is that you can re-use code without having to generate different classes for each data type they must handle, while still getting type-safety checking performed when referencing the class and its methods. The equivalent syntax in VB.NET to declare a generic class is: Public Class MyStructure(Of T) ... implementation here ... End Class You can then create an instance of this class in VB.NET for integer values using: Dim theStruct As New MyStructure(Of Int32)() The System.Collections.Generic namespace contains some useful classes for working with generic code including the generic List class (although you can use other collection classes as a basis for your own generic lists). Listing 15.11 shows the declaration of a very simple class called Point (in the file Point.cs of the example) that stores the X and Y coordinates of a point. The Custom Point Class
The GetGenericList method of the file Ch15DataProcess uses the Point class when you select the Generic List option in the main page. The method code (shown in Listing 15.12) creates a new generic List, adds some new Point instances to it, then generates a serialized representation of the contents using a BinaryFormatteras you saw in the previous section. Generating a Serialized Representation of a Generic List
For more information on generics in C#, see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/csharp_generics.asp and http://msdn.microsoft.com/msdnmag/issues/03/09/NET/default.aspx. For VB.NET, take a look at http://msdn.microsoft.com/msdnmag/issues/04/09/AdvancedBasics/ and http://msdn.microsoft.com/library/en-us/dnhcvs04/html/vs04k1.asp |
- Comment