Chapter 7. Arrays, Files, and Exceptions in C#

C# makes handling arrays and files extremely easy and introduces exceptions to simplify error handling.

Arrays

In C#, all arrays are zero based. If you declare an array as


int[] x = new int[10];

such arrays have 10 elements, numbered from 0 to 9. Thus, arrays are in line with the style used in C, C++, and Java.


const int MAX = 10;
float[] xy = new float[MAX];
for (int i = 0; i < MAX; i++ ) {
       xy[i] = i;
}

You should get into the habit of looping through arrays from zero to the array bounds minus one, as we did in the preceding example.

All array variables have a length property so you can find out how large the array is.


float[] z = new float[20];
for (int j = 0; j< z.Length ; j++) {
       z[j] = j;
}

Arrays in C# are dynamic and space can be reallocated at any time. To create a reference to an array and allocate it later within the class, use this syntax.


float[] z;               //declare here
z = new float[20];       //create later

Collection Objects

The System.Collections namespace contains a number of useful variable-length array objects you can use to add and obtain items in several ways.

ArrayLists

The ArrayList object is essentially a variable-length array that you can add items to as needed. The basic ArrayList methods allow you to add elements to the array and fetch and change individual elements.


float[] z = {1.0f, 2.9f, 5.6f} ;
ArrayList arl = new ArrayList ();
for (int j = 0; j< z.Length ; j++) {
       arl.Add (z[j]);
}

The ArrayList has a Count property you can use to find out how many elements it contains. You can then move from 0 to that count minus one to access these elements, treating the ArrayList just as if it were an array.


for (j = 0; j < arl.Count ; j++) {
       Console.WriteLine (arl[j]);
}

You can also access the members of ArrayList objects sequentially, using the foreach looping construct, without having to create an index variable or know the length of the ArrayList.


foreach (float a in arl) {
       Console.WriteLine (a);
}

You can also use the methods of the ArrayList shown in Table 7-1.

An object fetched from an ArrayList is always of type object. This means you usually need to cast the object to the correct type before using it.


float x = (float) arl[j];

Table 7-1. ArrayList Methods

image

Hashtables

A Hashtable is a variable-length array where every entry can be referred to by a key value. Typically, keys are strings of some sort, but they can be any sort of object. Each element must have a unique key, although the elements themselves need not be unique. Hashtables are used to allow rapid access to one of a large and unsorted set of entries and can also be used by reversing the key and the entry values to create a list where each entry is guaranteed to be unique.


Hashtable hash = new Hashtable ();
float freddy = 12.3f;
hash.Add ("fred", freddy);  //add to table
//get this one back out
float temp = (float)hash["fred"];

Note that like the ArrayList, we must cast the values we obtain from a Hashtable to the correct type. Hashtables also have a count property, and you can obtain an enumeration of the keys or of the values.

SortedLists

The SortedList class maintains two internal arrays, so you can obtain the elements either by zero-based index or by alphabetic key.


float sammy = 44.55f;
SortedList slist = new SortedList ();

slist.Add ("fred", freddy);
slist.Add ("sam", sammy);
//get by index
float newFred = (float)slist.GetByIndex (0);
//get by key
float newSam = (float)slist["sam"];

You will also find the Stack and Queue objects in this namespace. They behave much as you’d expect, and you can find their methods in the system help documentation.

Exceptions

Error handling in C# is accomplished using exceptions instead of other, more awkward kinds of error checking. The thrust of exception handling is that you enclose the statements that could cause errors in a try block and then catch any errors using a catch statement.


try {
  //Statements
}
catch (Exception e) {
  //do these if an error occurs
}
finally {
   //do these anyway
}

Typically, you use this approach to test for errors around file handling statements, although you can also catch array index out-of-range statements and a large number of other error conditions. The way this works is that the statements in the try block are executed, and if there is no error, control passes to the finally statements if any and then out of the block. If errors occur, control passes to the catch statement, where you can handle the errors, and then control passes on to the finally statements and then out of the block.

The following example shows testing for any exception. Since we are moving one element beyond the end of the ArrayList, an error will occur.


try {
       //note- one too many
       for(int i = 0; i <= arl.Count ; i++)
              Console.WriteLine (arl[i]);
}
catch(Exception e) {
              Console.WriteLine (e.Message );
}

This code prints out the error message and the calling locations in the program and then goes on.


0123456789Index was out of range.
Must be non-negative and less than the size of the collection.
Parameter name: index
   at System.Collections.ArrayList.get_Item(Int32 index)
   at arr.Form1..ctor() in form1.cs:line 58

By contrast, if we do not catch the exception, we will get an error message from the runtime system, and the program will exit instead of going on.

Some of the more common exceptions are shown in Table 7-2.

Table 7-2. Common Exception classes in C#

image

Multiple Exceptions

You can also catch a series of exceptions and handle them differently in a series of catch blocks.


try {
       for(int i =0; i<= arl.Count ; i++) {
              int k = (int)(float)arl[i];
              Console.Write(i + " "+ k / i);
       }
}
catch(DivideByZeroException e) {
       printZErr(e);
}
catch(IndexOutOfRangeException e) {
       printOErr(e);
}
catch(Exception e) {
       printErr(e);
}

This gives you the opportunity to recover from various errors in different ways.

Throwing Exceptions

You don’t have to deal with exceptions exactly where they occur. You can pass them back to the calling program using the throw statement. This causes the exception to be thrown in the calling program.


try {
//statements
}
catch(Exception e) {
       throw(e);    //pass on to calling program
}

Note that C# does not support the Java syntax throws, which allows you to declare that a method will throw an exception and that you therefore must provide an exception handler for it.

File Handling

The file handling objects in C# provide you with some fairly flexible methods of handling files.

The File Object

The File object represents a file and has useful methods for testing for a file’s existence as well as renaming and deleting a file. All of its methods are static, which means that you do not (and cannot) create an instance of File using the new operator. Instead, you use its methods directly.


if (File.Exists ("Foo.txt"))
       File.Delete ("foo.txt");

You can also use the File object to obtain a FileStream for reading and writing file data.


//open text file for reading
       StreamReader ts = File.OpenText ("foo1.txt");

//open any type of file for reading
       FileStream fs = File.OpenRead ("foo2.any");

These are some of the more useful File methods.

image

Reading a Text File

To read a text file, use the File object to obtain a StreamReader object. T hen use the text stream’s read methods.


StreamReader ts = File.OpenText ("foo1.txt");
String s =ts.ReadLine ();

Writing a Text File

To create and write a text file, use the CreateText method to get a StreamWriter object.


//open for writing
       StreamWriter sw = File.CreateText ("foo3.txt");
       sw.WriteLine ("Hello file");

If you want to append to an existing file, you can create a StreamWriter object directly with the Boolean argument for append set to true.


//append to text file
StreamWriter asw = new StreamWriter ("foo1.txt", true);

Exceptions in File Handling

A large number of the most commonly occurring exceptions arise in handling file input and output. You can get exceptions for illegal filenames, files that do not exist, directories that do not exist, illegal filename arguments, and file protection errors. Thus, the best way to handle file input and output is to enclose file manipulation code in Try blocks to assure yourself that all possible error conditions are caught and thus prevent embarrassing fatal errors. All of the methods of the various file classes show in their documentation which methods they throw. You can be confident that you will catch all of them by just catching the general Exception object, but if you need to take different actions for different exceptions, you can test for them separately.

For example, you might open text files in the following manner.


try {
//open text file for reading
       StreamReader ts = File.OpenText ("foo1.txt");
       String s =ts.ReadLine ();
}
catch(Exception e ) {
       Console.WriteLine (e.Message );
}

Testing for End of File

There are two useful ways to make sure you do not pass the end of a text file: (1) look for a null exception and (2) look for the end of a data stream. When you read beyond the end of a text file, no error occurs and no end of file exception is thrown. However, if you read a string after the end of a file, it will return as a null value. You can use this to create an end-of-file function in a file reading class.


private StreamReader rf;
private bool eof;
//------------
public String readLine () {
       String s = rf.ReadLine ();
       if(s == null)
              eof = true;
       return s;
}
//------------
public bool fEof() {
       return eof;
}

The other way to ensure that you don’t read past the end of a file is to peek ahead using the Stream’s Peek method. This returns the ASCII code for the next character, or a –1 if no characters remain.


public String read_Line() {
       String s = ""
       if (rf.Peek() > 0) {
              s = rf.ReadLine ();
       }
       else {
              eof=true;
       }
       return s;
}

A csFile Class

It is sometimes convenient to wrap these file methods in a simpler class with easy-to-use methods. We have done that here in the csFile class. We’ll be using this convenience class in some of the examples in later chapters.

We can include the filename and path in the constructor, or we can pass it in using the overloaded OpenForRead and OpenForWrite statements.


public class csFile
       {
              private string fileName;
              StreamReader ts;
              StreamWriter ws;
              private bool opened, writeOpened;
              //-----------
              public csFile() {
                     init();
              }
              //-----------
              private void init() {
                     opened = false;
                     writeOpened = false;
              }
              //-----------
              public csFile(string file_name)   {
                     fileName = file_name;
                     init();
              }

We can open a file for reading by either of two methods: one that includes the filename and one that uses a filename in the argument.


public bool OpenForRead(string file_name){
       fileName = file_name;
       try {
              ts = new StreamReader (fileName);
              opened=true;
       }
       catch(FileNotFoundException e) {
              return false;
       }
       return true;
}
//-----------
public bool OpenForRead() {
       return OpenForRead(fileName);
}

You can then read data from the text file using a readLine method.


public string readLine() {
       return ts.ReadLine ();
}

Likewise, the following methods allow you to open a file for writing and write lines of text to it.


public void writeLine(string s) {
       ws.WriteLine (s);
}
//-----------
public bool OpenForWrite() {
       return OpenForWrite(fileName);
}
//-----------
public bool OpenForWrite(string file_name) {
       try{
              ws = new StreamWriter (file_name);
              fileName = file_name;
              writeOpened = true;
              return true;
       }
       catch(FileNotFoundException e) {
              return false;
       }
}

We’ll use this simplified file method wrapper class whenever we need to read in a file.

Program on the CD-ROM

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset