Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp .NET Programming (2002) [eng]

.pdf
Скачиваний:
58
Добавлен:
16.08.2013
Размер:
7.98 Mб
Скачать

Listing 6.3 shows the complete code for the example, including throwing and catching the exception related to cockroaches.

Listing 6.3: Throwing and Catching an Exception

private void btnExceptions_Click(object sender, System.EventArgs e) { int i; double d;

try {

if (txtIn.Text == "cockroaches") {

throw(new ApplicationException("roaches"));

}

i = Convert.ToInt16(txtIn.Text); d = 42 / i;

}

catch (DivideByZeroException) {

MessageBox.Show("You are naughty to even think of dividing by zero!", "Exceptions", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

catch (FormatException) {

MessageBox.Show("Please enter an integer!", "Exceptions", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

catch (Exception excep) {

if (excep.Message == "roaches") {

MessageBox.Show("You have roaches in your program!", "Exceptions", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

else { MessageBox.Show(excep.Message); MessageBox.Show(excep.ToString());

}

}

finally {

MessageBox.Show("You can't stop me from being executed!", "Exceptions",

MessageBoxButtons.OK, MessageBoxIcon.Information);

}

}

When the program is run, 'cockroaches' entered in the TextBox, and the button clicked, the exception is thrown, and the bad news is out (Figure 6.11)!

Figure 6.11: This 'cockroach' exception illustrates that you can throw and catch custom exceptions.

Exception Objects

So far in this discussion, exceptions have been used only as sentinels-the presence of the exception signals the error like a canary falling ill in a mine. It's worth thinking a little about the nature of an exception object, because they can be used for more than this.

The first thing you should know about the Exception object and the classes that inherit from it is that (for the most part) the subclasses do not vary from Exception by implementing additional members or functionality. This means that-by and large-the only difference between the parent and child classes is the name of the class. (An important exception to this is SqlException, which is thrown when SQL Server returns a rather specific warning or error.) The implication is that if you have a compelling reason to add additional information to the exception class, you certainly can, but it should still make sense to deal with your exception as a generic exception (meaning, no matter how much information you add, you should still implement a meaningful "message" property so that programmers can throw and catch it in the normal fashion).

Table 6.7 shows some of the commonly used properties and methods of the Exception object.

Table 6.7: Commonly Used Properties and Methods of the Exception Object

Property

 

Purpose

or

 

 

Method

 

 

 

 

 

HelpLink

 

A link to the help file associated with the exception.

 

 

 

Message

 

Gets a message that describes the current exception.

StackTrace A string that contains the stack trace immediately before the exception was thrown.

TargetSite The method that threw the current exception.

ToString A string that contains the name of the exception and a great deal of other information, such as the error message and the stack trace.

In general, the Exception class has two subclasses: ApplicationException and SystemException. In theory, ApplicationExceptions are created by your application, and SystemExceptions are created by the run-time (CLR) and operating environment.

As you would expect, there are a great many subclasses of SystemExceptions, some of which you've seen in the examples earlier in this section (for example, FormatException). Most of these exception classes are the members of the System namespace, but others are located further down the namespace tree (for example, IOException is a member of System.IO rather than System directly).

The best place to learn about individual exceptions and their class relationships is the Object Browser (details of which were explained in Chapter 5).

Conclusion

This chapter has covered a great deal of ground and explained a great deal of the syntax that you need to successfully program in C#. Great flights of fancy and soundly engineered heroic structures cannot be created without good foundations; with this material under your belt, you can build wonderful things!

I started this chapter with Miranda's wonder at the 'goodly creatures' in her 'brave new world.' Of course, the joke in The Tempest is that she had seen few people of any kind. One wonders whether they soon became commonplace to her. Perhaps she picked up the magic book belonging to Prospero and began to create new worlds and visions… which is my hope for what you will do with the rudiments of C# syntax presented in this chapter

Chapter 7: Arrays, Indexers, and

Collections

Overview

Creating an array

Arrays of structs

Multidimensional and jagged arrays

Changing the lower bound of a C# array

Creating a class with an indexer

Working with collection classes

Pushing and popping stacks

Queues

Dynamic resizing with ArrayList

Implementing a key/value lookup

In the real world, programming usually involves groups of objects. Arrays are specifically designed to store groups of objects, with the object being retrieved using an index value. Collections-and the structures based on collections in C#, such as queues, ArrayLists, and much more-are an alternative mechanism for grouping and coping with multiple objects.

In Chapter 4, 'Building a Better Windows Interface,' you learned about working with the Items collection that is used with ListBoxes. It's worth recalling the material in Chapter 4, because I assume in this chapter that you know how to work with ListBoxes and the ListBox items collection. Also, the techniques for working with this collection is basically similar to the techniques explained in this chapter. (In fact, as you may have already suspected, the items in a ListBox are an instance of a collection class.)

If you don't know how to deal with multiple items in arrays (and other classes designed for use with multiples, such as those based on the System.Collection classes), then your programs will never scale-or be of much use when dealing in an automated fashion with the large amount of data presented by the real world.

It's important to pick the right underlying structure for dealing with groups of objects, and you also need to know how to work with the structure you've selected. This chapter covers both aspects of dealing with arrays, collections, and related classes.

Arrays

In C#, an array is an object that is used to store objects of the same type and provides access to the objects using an index. You should know that-just as the string keyword is used to create an object of type System.String-the syntax and expressions used to create arrays actually create an object based on the System.Array class. This means that you can use the membersmethods and properties-provided by the Array class when you work with arrays. Most Array methods are shown in Table 7.1, and Array properties are shown in Table 7.2.

 

 

Table 7.1: Array Methods

Method

 

Meaning

 

 

 

BinarySearch

 

Searches a one-dimensional sorted array.

 

 

 

Clear

 

Sets a range of elements in an array to 0, to false, or to a null reference,

 

 

depending on the type of the array elements.

 

 

 

Copy

 

Copies a range of elements from one array to another.

 

 

 

CopyTo

 

Copies all the elements of a one-dimensional array to another one-

 

 

dimensional array, starting at the specified destination array index in the

 

 

new array.

 

 

 

CreateInstance

 

A static method that explicitly instantiates and initializes a new array

 

 

instance. Note that this method allows you to specify a lower bound for the

 

 

array that is non-zero (see example later in this chapter).

 

 

 

GetLength

 

Gets the number of elements in a specified dimension of the array.

GetLowerBound Gets the lower bound of the specified dimension in the array. Note that if the array has been created using normal syntaxe.g., not using CreateInstance the lower bound of each dimension will be 0.

GetUpperBound

 

Gets the upper bound of the specified dimension in the array.

 

 

 

GetValue

 

Gets the value of the specified element in the array.

 

 

 

IndexOf

 

Returns the index of the first occurrence of a value in a one-dimensional

 

 

array (or in a portion of the array).

 

 

 

 

Table 7.1: Array Methods

 

 

 

 

 

Method

 

 

 

Meaning

 

 

 

 

 

Initialize

 

 

 

Initializes every element of a value-type array by calling the default

 

 

 

 

constructor of the value type.

 

 

 

 

 

LastIndexOf

 

 

 

Returns the index of the last occurrence of a value in a one-dimensional

 

 

 

 

array (or in a portion of the array).

 

 

 

 

 

Reverse

 

 

 

Reverses the order of the elements in a one-dimensional array (or in a

 

 

 

 

portion of the array).

 

 

 

 

 

SetValue

 

 

 

Sets the specified element in the current array to the specified value.

 

 

 

 

 

Sort

 

 

 

Sorts the elements in a one-dimensional array.

 

 

 

 

 

 

 

 

 

Table 7.2: Array Properties

 

 

 

Property

 

Returns

 

 

 

IsFixedSize

 

A Boolean value indicating whether the array has a fixed size.

 

 

 

IsReadOnly

 

A Boolean value indicating whether the array is read-only.

 

IsSynchronized

 

A Boolean value indicating whether the array is thread-safe.

 

 

 

Length

 

The total number of elements in all the dimensions of an array.

 

 

 

Rank

 

The number of dimensions of an array.

 

 

 

SyncRoot

 

An object that can be used to synchronize access to the array.

 

 

 

 

 

Arrays in C# are, for the most part, zero-indexed-meaning that the array indices start at 0 (but see the example later in this chapter that shows how to start an array at another index).

One-dimensional arrays can be thought of as a table with one column that can be accessed using an index. Multidimensional arrays use multiple indices to access their values, so a twodimensional array can be pictured as a table with rows and columns. In a jagged array-also called an array of arrays-each 'row' in the array is itself an array, with a potentially different size than the arrays making up the other rows.

Boxing and Unboxing

You may have observed that types of a class that are derived from a class can be assigned to an array of that class, even though, as I mentioned above, an array is used to store objects of 'the same type.' Specifically, objects of any type can be stored in an array of objects (since all types are derived from the object type).

For example, the following is legal code, and creates an array of objects that are assigned three different types:

int theInt; string theString; Button1 button1; object [] stuff = new object [3];

stuff [0] = theInt; stuff [1] = theString; stuff [2] = button1;

What has actually happened is that the various types have been implicitly converted to type object. (If you look at the members of each element of the stuff [] array, you'll find the members of an object type.) However, the extended information relating to the derived type has been preserved. This is called "boxing."

To reverse the process, and 'unbox' the types stored as objects, you need to explicitly cast the element of the object array to the original type. For example:

int newInt = (int) stuff [0];

string newString = (string) stuff [1]; Button button2 = (Button) stuff [2];

Creating an Array

Let's start with one-dimensional arrays. The process of creating an array is a three-step dance (although these steps can be combined, as we'll see in a moment):

The array must be declared.

Next, it is instantiated.

Finally, it is initialized with values.

To declare an array, follow a type with square brackets and continue with the array's identifier. For example, you could declare an integer array numbers and a string array names as follows:

int [] numbers; // declares integer array string [] names; // declares string array

To instantiate the array, as you'd expect, the new keyword is used. The statement

numbers = new int [3];

instantiates a new three-element, zero-based array with the previously declared variable numbers. (The elements are numbers[0], numbers[1], and numbers[2].)

The two statements can be combined into one, so that you can instantiate while you declare:

int [] numbers = new int[3];

At this point, you should know that the three elements of the integer array numbers have been initialized to 0. (Arrays of reference-type elements are initialized to a null reference.)

Note You can use a constant or variable rather than a literal to size the dimensions of an array.

As it turns out, you can initialize the array at the same time as you declare and instantiate it, by placing the initial values within curly braces. Here are one-step examples that declare, instantiate, and initialize an integer array and a string array:

int [] numbers = new int[3] {3,1,4};

string [] names = new string[3] {"Tom", "Dick", "Harry"};

There are a couple of shorthand ways to say the same thing. If you are initializing an array, you can leave off the dimension, in which case it is created with the number of elements specified by the initialization. So these statements create three-element arrays just like the preceding ones:

int [] numbers = new int[] {3,1,4};

string [] names = new string[] {"Tom", "Dick", "Harry"};

If you really prefer to be terse, you can also leave off the new part of the statement (once again, assuming you've provided initial values). The compiler is smart enough to know that it is implied. So here's the shortest way to declare, instantiate, and initialize these two arrays:

int [] numbers = {3,1,4};

string [] names = {"Tom", "Dick", "Harry"}

Moving on, let's try a little example of creating and using an array. Let's suppose we want an array to store the first seven numbers in the Fibonacci series, which comes up in art, nature, mathematics, and mysticism. Here's the shorthand way to create that array and stuff the right values into it:

int [] fibArray = new int[7] {1,1,2,3,5,8,13};

Let's say, instead, that we are fond of iteration. As you probably know, the first two elements of the Fibonacci series are 1; after that, the element n in the series is equal to the sum of the elements (n - 1) and (n - 2).

First, we can declare and instantiate the array with seven zero-based elements:

int [] fibArray = new int[7];

Next, we can assign the first two values in the series.

fibArray[0] = fibArray[1] = 1;

Finally, we can use iteration to assign the rest of the values in the array:

for (int i = 2; i < 7; ++i)

fibArray[i] = fibArray[i - 1] + fibArray[i - 2];

You can use a message box to display an element of the array to make sure that this has all worked correctly, as shown in Listing 7.1.

Listing 7.1: Creating an Integer Array and Displaying an Element

private void btnCreate_Click(object sender, System.EventArgs e) { // int [] fibArray = new int[7] {1,1,2,3,5,8,13};

int [] fibArray = new int[7]; fibArray[0] = fibArray[1] = 1; for (int i = 2; i < 7; ++i)

fibArray[i] = fibArray[i - 1] + fibArray[i - 2];

string fifthFib = fibArray[4].ToString();

MessageBox.Show("The fifth number in the Fibonacci series is " + fifthFib,

"Arrays", MessageBoxButtons.OK, MessageBoxIcon.Information);

}

foreach Statement

The foreach statement is a simple way to iterate through the elements of an array. If we continue with our array of Fibonacci numbers, it's easy to use a foreach statement

foreach (int fib in fibArray) { lstFib.Items.Add(fib.ToString()); }

to cycle through the Fibonacci array and, one by one, add the string representation of each element to a ListBox (named lstFib).

The complete revised click event procedure that creates the array and then uses foreach to cycle through it, adding the elements to the ListBox, is shown in Listing 7.2. If you run the code and then click the button, the Fibonacci numbers will appear in the ListBox as shown in Figure 7.1.

Figure 7.1: The foreach statement can be used to display array elements in a ListBox. Listing 7.2: Adding the Integer Array to a ListBox

private void btnCreate_Click(object sender, System.EventArgs e) { int [] fibArray = new int[7];

fibArray[0] = fibArray[1] = 1; for (int i = 2; i < 7; ++i)

fibArray[i] = fibArray[i - 1] + fibArray[i - 2]; string fifthFib = fibArray[4].ToString();

foreach (int fib in fibArray) { lstFib.Items.Add(fib.ToString());

}

}

By the way, if you'd stored the Fibonacci numbers as elements in a string array in the first place (rather than as integers), you could add the elements in the array to the ListBox with a single AddRange method call. Here's the declaration and instantiation of the Fibonacci array as string:

string [] fibArray = new string[7];

followed by the assignment of the first two numbers, as strings, of course, in the series:

fibArray[0] = fibArray[1] = "1";

To pull off the iteration, we have to get a little tricky in our conversions. First, the n - 1 and n - 2 elements are each converted to integer. The integers are added together, and the result converted back to string:

for (int i = 2; i < 7; ++i) {

fibArray[i] = (Convert.ToInt16(fibArray[i - 1]) + Convert.ToInt16(fibArray[i - 2])).ToString(); }

Finally, the payoff-it takes only a single statement to fill the ListBox:

lstFib.Items.AddRange (fibArray);

Listing 7.3 shows the process of filling a string array with strings representing the Fibonacci series and adding them to a ListBox.

Listing 7.3: Using AddRange to Add a String Array to a ListBox

string [] fibArray = new string[7]; fibArray[0] = fibArray[1] = "1"; for (int i = 2; i < 7; ++i) {

fibArray[i] = (Convert.ToInt16(fibArray[i - 1]) + Convert.ToInt16(fibArray[i - 2])).ToString();

}

lstFib.Items.AddRange (fibArray);

Arrays of Structs

Who says the elements of your array have to be simple value types? Often, it makes sense to define classes or structs that are used as the template for each element of an array.

As an example, let's go back to the Employee struct defined in Chapter 6, "Zen and Now: The C# Language":

public struct Employee {

public string fullName, rank;

public long SSN;

public Employee(string fullName, string rank, long SSN) { this.fullName = fullName;

this.rank = rank; this.SSN = SSN;

}

}

It's easy to create instances based on this struct. For example:

Employee DavisN = new Employee("Nicholas Davis", "Opera Singer", 12345678);

Next, we can declare, instantiate, and initialize a three-element array with Employee structs for elements (assuming all three struct elements are defined):

Employee [] theRoster = {HopperK, DavisN, DavisJ};

As I explained earlier in this chapter, this statement is shorthand for the more formal

Employee [] theRoster = new Employee [3] {HopperK, DavisN, DavisJ};

Next, if you'd like, you can display some of the information stored in a struct element:

MessageBox.Show(theRoster[1].fullName + " is an " + theRoster[1].rank + ".",

"Arrays", MessageBoxButtons.OK, MessageBoxIcon.Information);

Finally, it's easy to use a foreach statement to iterate through the array of structs and add specific field information to a ListBox:

foreach (Employee emp in theRoster) { lstRoster.Items.Add(emp.fullName);

}

If you run the code shown in Listing 7.4, the contents of the fullname field of each struct in the array will be added to a ListBox (Figure 7.2).

Figure 7.2: You can create arrays of structs, and display struct elements.