case "casement" : case "c" :
handleCasement ();
break ; case "standard" : case "s" :
handleStandard () ; break ;
case "patio" : case "P" :
handlePatio () ; break ;
default :
Console.WriteLine ( "Invalid command" ) ; break ;
}
The above switch will select a particular option if the user types the full part of the name or just the initial letter. If you want to perform selection based on strings of text like this I’d advise you to take a look at the ToUpper and ToLower methods provided by the string type. These can be used to obtain a version of a string which is all in upper or lower case, which can make the testing of the commands much easier:
switch (command.ToUpper()) {
case "CASEMENT" : case "C" :
....
Programmer’s Point: switches are a good idea
Switches make a program easier to understand as well as quicker to write. It is also easier to add extra commands if you use a switch since it are just a matter of putting in another case. However, I'd advise against putting large amounts of program code into a switch case. Instead you should put a call to a method as I have above.
3.6 Using Files
If you want your program to be properly useful you have to give it a way of storing data when it is not running. We know that you can store data in this way; that is how we have kept all the programs we have created so far, in files.
Files are looked after by the operating system of the computer. What we want to do is use C# to tell the operating system to create files and let us access them. The good news is that although different operating systems use different ways to look after their files, the way in which you manipulate files in C# is the same for any computer. We can write a C# program which creates a file on a Windows PC and then use the same program to create a file on a UNIX system with no problems.
3.6.1 Streams and Files
C# makes use of a thing called a stream to allow programs to work with files. A stream is a link between your program and a data resource. Data can flow up or down your stream, so that streams can be used to read and write to files. The stream is the thing that links your program with the operating system of the computer you are using. The operating system actually does the work, and the C# library you are using will convert your request to use streams into instructions for the operating system you are using at the time:
Creating Programs Using Files
A C# program can contain an object representing a particular stream that a programmer has created and connected to a file. The program performs operations on the file by calling methods on the stream object to tell it what to do.
C# has a range of different stream types which you use depending on what you want to do. All of the streams are used in exactly the same way. In fact you are already familiar with how streams are used, since the Console class, which connects a C# program to the user, is implemented as a stream. The ReadLine and WriteLine methods are commands you can give any stream that will ask it to read and write data.
We are going to consider two stream types which let programs use files; these are the StreamWriter and StreamReader types.
3.6.2 Creating an Output Stream
You create a stream object just like you would create any other one, by using new.
When the stream is created it can be passed the name of the file that is to be opened.
StreamWriter writer ;
writer = new StreamWriter("test.txt");
The variable writer will be made to refer to the stream that you want to write into.
When the new StreamWriter is created the program will open a file called
test.txt for output and connect the stream to it. If this process fails for any reason, perhaps your operating system is not able/allowed to write to the file or the name is invalid, then the action will fail with an appropriate exception.
Note however that this code does not have a problem if the file test.txt already exists. All that happens is that a brand new, empty, file is created in place of what was there. This is potentially dangerous. It means that you could use the two statements above to completely destroy the contents of an existing file, which would be bad. Most useful programs ask the user whether or not an existing file should be overwritten, you will find out later how to do this.
3.6.3 Writing to a Stream
Once the stream has been created it can be written to by calling the write methods it provides.
writer.WriteLine("hello world");
The above statement calls the WriteLine method on the stream to make it write the text ―hello world‖ into the file test.txt. This is exactly the same technique that is used to write information to the console for the user to read. In fact you can use all the writing features including those we explored in the neater printing section to format your output.
Each time you write a line to the file it is added onto the end of the lines that have already been written. If your program got stuck writing in an infinite loop it is possible that it might fill up the storage device. If this happens, and the write cannot be
performed successfully, the call of WriteLine will throw an exception. A properly written program should probably make sure that any exceptions like this (they can also be thrown when you open a file) are caught and handled correctly.
C#
program
File in
Windows
stream
Creating Programs Using Files
3.6.4 Closing a Stream
When your program has finished writing to a stream it is very important that the stream is explicitly closed using the Close method:
writer.Close();
When the Close method is called the stream will write out any text to the file that is waiting to be written and disconnect the program from the file. Any further attempts to write to the stream will fail with an exception. Once a file has been closed it can then be accessed by other programs on the computer, i.e. once the close has been performed you can use the Notepad program to open test.txt and take a look at what is inside it. Forgetting to close a file is bad for a number of reasons:
It is possible that the program may finish without the file being properly closed. In this situation some of the data that you wrote into the file will not be there.
If your program has a stream connected to a file other programs may not be able to use that file. It will also be impossible to move or rename the file.
An open stream consumes a small, but significant, part of operating resource.
If your program creates lots of streams but does not close them this might lead to problems opening other files later on.
So, close the file or suffer the consequences.
3.6.5 Streams and Namespaces
If you rush out and try the above bits of code you fill find that they don’t work. Sorry about that. There is something else that you need to know before you can use the StreamWriter type. Like lots of the objects that deal with input and output, this object is defined in the System.IO namespace. We have touched on namespaces before, when we reflected on the need to have the statement using System; at the start of our C# programs. Now we need to find out more about them.
Namespaces are all to do with finding resources. The C# language provides the keywords and constructions that allow us to write programs, but on top of this there are a whole lot of extra resources supplied with a C# installation. These resources are things like the Console object that lets us read and write text to the user. A C#
installation actually contains many thousands of resources, each of which must be uniquely identified. If you were in charge of cataloguing a huge number of items you would find it very helpful to lump items into groups. Museum curators do this all the time. They put all the Roman artefacts in one room, and the Greek ones in another. The designers of the C# language created a namespace facility where programmers can do the same kind of thing with their resources.
A namespace is, quite literally, a ―space where names have meaning‖. The full name of the Console class that you have been using to write text to the user is
System.Console. That is, the Console class in the System namespace. In fact it is quite OK to use this full form in your programs:
System.Console.WriteLine("Hello World");
The above uses the fully qualified name of the console resource and calls the method WriteLine provided by that resource. However, we’ve not had to use this form because at the start of our programs we have told the compiler to use the System namespace to find any names it hasn’t seen before. The using keyword allows us to tell the compiler where to look for resources.
using System;
This statement tells the compiler to look in the System namespace for resources. Once we have specified a namespace in a program file we no longer need to use the fully
Creating Programs Using Files
item it hasn’t seen before it will automatically look in the namespaces it has been told to use. In other words, when the compiler sees the statement:
Console.WriteLine("Hello World");
- it knows to look in the System namespace for that object so that it can use the WriteLine method on it. If the programmer miss-types the class name:
Consle.WriteLine("Hello World");
- the compiler will look in the System namespace, fail to find an object called Console and generate a compilation error.
This is the same error that you will get if you try to use the StreamWriter class without telling the compiler to look in the System.IO namespace. In other words, to use the file handing classes you will need to add the following statement at the very top of your program:
using System.IO;
It is possible to put one namespace inside another (just like a librarian would put a cabinet of Vases in the Roman room which he could refer to as Roman.Vases) and so the IO namespace is actually held within the System namespace. However, just because you use a namespace, this does not imply that you use all the namespaces defined within it, and so you must include the above line for file handling objects to be available.
Namespaces are a great way to make sure that names of items that you create don’t clash with those from other programmers. We will see how you can create your own namespaces later on.
3.6.6 Reading from a File
Reading from a file is very similar to writing, in that the program will create a stream to do the actual work. In this case the stream that is used is a StreamReader.
TextReader reader = new StreamReader("Test.txt");
string line = reader.ReadLine();
Console.WriteLine (line);
reader.Close();
The above program connects a stream to the file Test.txt, reads the first line from the file, display it on the screen and then close the stream. If the file can’t be found then the attempt to open it will fail and the program will throw an exception.
Detecting the End of an Input File
Repeated calls of ReadLine will return successive lines of a file. However, if your program reaches the end of the file the ReadLine method will return an empty string each time it is called. Fortunately the StreamReader object provides a property called EndOfStream that a program can use to determine when the end of the file has been reached. When the property becomes true the end of the file has been reached.
StreamReader reader;
reader = new StreamReader("Test.txt");
while (reader.EndOfStream == false) {
string line = reader.ReadLine();
Console.WriteLine(line);
}
reader.Close();
The above program will open up the file test.txt and display every line in the file on the console. The while loop will stop the program when the end of the file is reached.
Creating Programs Using Files
3.6.7 File Paths in C#
If you have used a computer for a while you will be familiar with the idea of folders (sometimes called directories). These are used to organise information we store on the computer. Each file you create is placed in a particular folder. If you use Windows you will find that there several folders created for you automatically. One can be used for Documents, another for Pictures and another one for Music. You can create your own folders inside these (for example Documents\Stories).
The location of a file on a computer is often called the path to the file. The path a file can be broken into two parts, the location of the folder and the name of the file itself. If you don’t give a folder location when you open a file (as we have been doing with the file Test.txt) then the system assumes the file that is being used is stored in the same folder as the program which is running. In other words, if you are running the program FileRead.exe in the folder MyProgs then the above programs will assume that the file Test.txt is in the MyProgs folder too.
If you want to use a file in a different folder (which is a good idea, as data files are hardly ever held in the same place as programs run from) you can add path information to a filename:
string path;
path = @"c:\data\2009\November\sales.txt";
The above statements create a string variable which contains the path to a file called sales.txt. This file is held in the folder November, which is in turn held in the folder 2009, which is held in the folder data which is on drive C.
The backslash (\) characters in the string serve to separate the folders along the path to the file. Note that I have specified a string literal that doesn’t contain control characters (that is what the @ at the beginning of the literal means) as otherwise the \ characters in the string will get interpreted by C# as the start of a control sequence. If you have problems where your program is not finding files that you know are there, I’d advise you to make sure that your path separators are not getting used as control characters.
Creating Solutions Our Case Study: Friendly Bank
4 Creating Solutions
4.1 Our Case Study: Friendly Bank
The bulk of this section is based on a case study which will allow you to see the features of C# in a strong context. You are taking the role of a programmer who will be using the language to create a solution for a customer.
The program we are making is for a bank, the "United Friendly and Really Nice Bank of Lovely People ™", otherwise known as the Friendly Bank. We will be creating the entire bank application using C# and will be exploring the features of C# that make this easy.
It is unlikely that you will get to actually implement an entire banking system during your professional career as a programmer (although it might be quite fun – and probably rather lucrative). However, from a programming point of view it is an interesting problem and as we approach it we will uncover lots of techniques which will be useful in other programs that we might write.
Programmer’s Point: Look for Patterns
The number of different programs in the world is actually quite small. A lot of the things that our bank is going to do (store a large amount of information about a large number of individuals, search for information for a particular person, implement transactions that change the content of one or more items in the system) are common to many other types of programs, from video games to robots.
4.1.1 Bank System Scope
The scope of a system is a description of the things that the system is going to do. This is also, by implication, a statement of what the system will not do. This equally as important, as a customer will not usually have clear idea of what you are doing and may well expect you to deliver things that you have no intention of providing. By setting out the scope at the beginning you can make sure that there are no unpleasant surprises later on.
At the moment we are simply concerned with managing the account information in the bank. The bank manager has told us that the bank stores information about each customer. This information includes their name, address, account number, balance and overdraft value. Other data items might be added later.
There are many thousands of customers and the manager has also told us that there are also a number of different types of accounts (and that new types of account are invented from time to time).
The system must also generate warning letters and statements as required.
Bank Notes
At the end of some sections there will be a description of how this new piece of C# will affect how we create our bank system.
These notes should put the feature into a useful context.