The Item Property allows a Product to be retrieved from, or added to, the collection using the product code: Public Default Property ItemCode as String as Product Implementing the GetEn
Trang 1Implementing the Add Method
The Add method allows a new Product to be added to the collection:
Public Sub Add( Item as Product )
If Item Is Nothing Then
throw new ArgumentException("Product cannot be null")
Implementing the Remove Method
The Remove method removes a Product from the collection The implementation of the method simply calls the
Remove method of the Hashtable:
Public Sub Remove(Item as Product)
_products.Remove(Item.Code)
End Sub
Implementing the Item Property
Trang 2The Item Property allows a Product to be retrieved from, or added to, the collection using the product code:
Public Default Property Item(Code as String) as Product
Implementing the GetEnumerator Method
So that our collection class can be used with the for each statements in Visual Basic.NET and C#, our collection class must have a method called GetEnumerator Although not strictly necessary, we also implement the IEnumerableinterface using the Visual Basic.NET Implements keyword This is good practice and requires very little work:
Public Class ProductCollection
Implements IEnumerable
'
' Implement an enumerator for the products
Trang 3Public Function GetEnumerator() As IEnumerator
Using the Collection Class
With the Product and ProductCollection classes created, we can use them just like the other collections we have seen in this chapter, but this time with no casting For example:
<%
Dim products As ProductCollection
Dim p As product
products = New ProductCollection()
p = New Product("CAR", "A New Car", 19999.99)
products.Add(p)
p = New Product("HOUSE", "A New House", 299999.99)
Trang 4products.Add(p)
p = New Product("BOOK", "A New Book", 49.99)
products(p.Code) = p
Response.Write("<h4>Product List</h4>")
For Each p In products
Response.Write (p.Code & ", " & p.Description & ", " & p.Price)
Response.Write("<br />")
Next
products.Remove( products("HOUSE") )
Response.Write("<h4>Product List (HOUSE removed)</h4>")
For Each p In products
Response.Write(p.Code & ", " & p.Description & ", " & p.Price)
Trang 5The output of this code is shown here:
Here is the complete ASP.NET code written in Visual Basic.NET for the Product and ProductCollection classes, as well as the ASP.NET page:
dim _code as string
dim _description as string
dim _price as Double
Trang 7' Define a Products Collection
Public Class ProductCollection
Implements IEnumerable
Trang 8dim _products as Hashtable
Public Sub New()
_products = new Hashtable()
End Sub
' Implement an enumerator for the products
Public Function GetEnumerator() As IEnumerator _
Trang 9If Item is Nothing Then
Throw New ArgumentException("Product can not be null")
products = New ProductCollection()
p = New Product("CAR", "A New Car", 19999.99)
Trang 10For Each p In products
Response.Write (p.Code & ", " & p.Description & ", " & p.Price)
Response.Write("<br />")
Next
products.Remove( products("HOUSE") )
Response.Write("<h4>Product List (HOUSE removed)</h4>")
For Each p In products
Response.Write(p.Code & ", " & p.Description & ", " & p.Price)
Trang 11products("CAR").Description)
%>
The equivalent code written in C# can be found in the code download
The DictionaryBase and CollectionBase Classes
The DictionaryBase and CollectionBase classes allow us to create a Hashtable or ArrayList collection that can validate, and therefore restrict, the types it contains It's a simple process to create our own collection class by deriving from these classes
This simple ASP.NET page defines a collection class called MyStringCollection, adds three strings and one integer, and then displays the contents:
Dim names As IList
Dim name As string
names = New MyStringCollection
Trang 12names.Add("Richard")
names.Add("Alex")
names.Add("Dave")
names.Add( 2002 )
For Each name In names
Response.Write( name & "<br />" )
Class MyStringCollection
Inherits CollectionBase
Overrides Protected Sub OnInsert(index as Integer, item as Object)
If Not (TypeOf item Is String) Then
Throw New ArgumentException("Collection only supports strings")
End If
Trang 13End Sub
End Class
The DictionaryBase class is used in the same way as the CollectionBase class and implements the IDictionaryand ICollection interfaces
The ReadOnlyCollectionBase Class
The ReadOnlyCollectionBase class provides functionality for exposing a read-only collection The class implements the ICollection and IEnumerable interface The items exposed are internally held in a protected ArrayListvariable called InnerList To use this class we have to derive our own class from it, and populate the contents of the InnerList array
When we use the IEnumerator interface directly (or any other enumerable type), if we do not know if an enumerator object supports the IDisposable interface we should always check once we have finished with it For example, in C# we might write:
IEnumerator e = c.GetEnumerator();
try
{
while (e.MoveNext())
Trang 14If we know that an enumerator object supports IDisposable we can call it directly:
IEnumerator e = c.GetEnumerator(); try
Trang 15Summary
In this chapter we've covered most of the interfaces and classes that comprise the System.Collections and System.Collection.Specialized namespaces We've examined the standard interfaces such as IEnumerable, IEnumerator, and ICollection, and we've discussed how these interfaces work together to provide a consistent way
of creating and using collections We've not covered every single collection class or method in this chapter, but hopefully you should have a pretty good feel for the type of functionality available
In this chapter we looked at:
How to use the standard collection classes such as ArrayList, Hashtable, Queue, and Stack
How to derive from collection base classes to quickly implement our own collection classes that restrict contained types
How to create strongly-typed collections that make using collections more intuitive for specific applications, which leads to more readable code thanks to fewer casts
In the next chapter, we'll look at the classes provided by the NET Framework class library for working with text files, sockets, and regular expressions
Trang 16Working with Other Base Classes
In the last chapter we introduced the NET Framework class library, and spent a fair amount of time looking at the collection classes it provides for dealing with common data structures such as lists, queues, and stacks In this chapter we're going to continue our examination of the class library, this time looking at the classes it provides for working with directories, files, regular expressions, and web requests
In this chapter we'll cover:
The contents of the file system, working with files and directories
Reading and writing data from backing stores such as the file system or memory
Retrieving data in a generic way using stream objects
Using regular expressions to parse text and extract values using captures
We'll start the chapter by looking at the classes used for working with the file system
Working with Directories and Files
The NET Framework class library makes working with directories and files an easy and painless experience It provides
an easy-to-understand set of classes located in the System.IO namespace These classes can be used to:
Retrieve and change information about directories and files
Manipulate paths, including combining them and extracting individual elements
Read and write bytes of data from generic streams such as files and memory buffers
It's important to understand early on that the classes in System.IO are not only designed for working with the file system They are designed to work with any number of backing stores that are accessed using stream objects
A backing store is the NET Framework term used to define a source which data can be read from or written to using a stream object Each backing store provides a stream object that is used to communicate with it For example, the
Trang 17FileStream class (the stream object) can be used to read and write data to the file system (the backing store), and the MemoryStream class can be used to read and write data to memory
All stream classes derive from a common base class Stream and just like the collection interfaces described in the previous chapter, once you know what the common System.IO classes are, and how they're organized, you'll find working with new data sources to be a breeze
Class Overview
The following classes are commonly used when working with directories, files, and streams:
Directory Provides static (shared) methods for enumerating directories and logical drives
DirectoryInfo Used to work with a specific directory and its sub-directories
File Provides static methods for working with files
FileInfo Used to work with a specific file
Stream Base class used to read from and write to a backing store, such as the file system or network StreamReader Used in conjunction with a stream to read characters from a backing store
StreamWriter Used in conjunction with a stream to write characters to a backing store
TextReader Abstract class used to define methods for reading characters from any source (backing store, string,
and so on)
Class Description
TextWriter Abstract class used to define methods for writing characters to any source (backing store, string, and so
on)
BinaryReader Used to read primitive types such as strings, integers, and Booleans from a stream
BinaryWriter Used to write primitive types such as strings, integers, and Booleans to a stream
FileStream Used to read and write data in the file system
MemoryStream Used to read and write data in a memory buffer
DirectoryInfo and Directory
The base class library provides two classes for working with directories: Directory and DirectoryInfo The Directory class contains a number of static methods (in VB NET these are known as shared methods) that can be used
to manipulate and query information about any directory The DirectoryInfo class contains a series of instance methods (also known as non-static or non-shared) and properties that can be used to manipulate and work with a single named directory
For the most part these classes have equivalent functionality, and can be used to:
Trang 18 Create and delete directories
Determine if a directory exists
Get a list of sub-directories and/or files for a given directory
Get information about directories, such as creation times and attributes, and make changes to that information
Get and set the current working directory (Directory class only)
Get a list of available drives (Directory class only)
Move directories
Although confusing at first, having two classes actually simplifies and increases the performance of our applications For example, if we wanted to determine whether or not a given directory existed, we could use the static Exists method of the Directory class as follows (written here in VB NET):
<%@ Page Language="VB" %><%@ Import Namespace="System.IO" %>
The constructor of the Directory class is declared as private, so it is not possible to instantiate an instance of the class
To check if a directory exists using the DirectoryInfo class, we have to instantiate an instance of the DirectoryInfoclass, passing the directory name we want to work with into the constructor Then we call the Exists property Using
VB NET we would write:
Trang 19<%@ Page Language="VB" %><%@ Import Namespace="System.IO" %>
<%
Dim dir As DirectoryInfo
dir = New DirectoryInfo("C:\Wrox")
If dir.Exists = True Then
Response.Write("C:\Wrox directory exists")
is much improved, as demonstrated by this additional line of code that displays the creation time of a directory (if it exists):
<%@ Page Language="VB" %><%@ Import Namespace="System.IO" %>
<%
Dim dir As DirectoryInfo
dir = New DirectoryInfo("C:\Wrox")
If dir.Exists = True Then
Response.Write("C:\Wrox directory exists")
Trang 20Response.Write("<br />Created: " & dir.CreationTime )
It looks neater, feels right, and can have performance benefits if the method was going to use the DirectoryInfo class
to do a lot of work Also, why create a new instance of the DirectoryInfo class when the caller might already have one?
Another, more subtle, benefit of using the DirectoryInfo class is that it will typically execute multiple operations against a single directory in an efficient manner Once instantiated, it can maintain state such as the creation time and last modification date of a directory Then, when members are used, such as the CreationTime property, this state can be used to provide the results The Directory class cannot do this It must go out and retrieve information about a directory each time a method is called Although traditionally this wasn't a terribly expensive operation, with the advent
of the CLR this type of operation requires code access permissions to be granted by the runtime, which means that the runtime has to ensure the code calling the method is allowed to know about the directory These checks can be relatively expensive to perform and their use should be minimized Accordingly, using the DirectoryInfo class wherever possible makes good coding sense The DirectoryInfo class performs different code access permission checks depending on the methods called While some methods will not cause permission checks, others, such as Delete, always will
File and FileInfo
We can use the File and FileInfo classes to discover information about files, as well as to get access to a stream object that allows us to read from and write to the contents of a file
The File and FileInfo classes provide equivalent functionality and can be used to:
Create, delete, open, copy, and move files (these classes are not used to read, write, append to, or close files)
Retrieve information about files, such as creation times and attributes, and to change that information
Like the Directory class, the File class has a series of static methods to manipulate or query information about a file The FileInfo class has a series of instance methods and properties that can be used to manipulate and work with a single named file
Trang 21Here is a simple (VB NET) code example that shows how to use the File class to determine if a file exists:
<%@ Page Language="VB" %><%@ Import Namespace="System.IO" %>
<%If File.Exists("C:\Wrox\Hello.txt") = True Then
Response.Write("C:\Wrox\Hello.Txt file exists")
Dim myfile As FileInfo
myfile = New FileInfo("C:\Wrox\Hello.Txt")
If myfile.Exists = True Then
Response.Write("C:\Wrox\Hello.Txt file exists")
Response.Write("<br />Created: " & myfile.CreationTime)
Else
Response.Write("<br />C:\Wrox\Hello.Txt file does not exist")