Using Common Reference Types - continued

johnmora's picture

Strings and StringBuilders

One of the built in reference types within the .NET Framework you will commonly use is the System.String type. System.String has a set of members for manipulating text. For example here is a simple quick method for finding and replacing text:

   1: string s = "this is some text to search";
   2: s = s.Replace("search", "replace");
   3: Console.Writeline(s);

The output of the preceding code will be: “this is some text to replace”. Strings of type System.String are unable to be changed and are referred to as “immutable”. Any changes to a string causes the runtime to create a new string abandoning the old one. This happens in the background. You can easily create a lot of temporary strings causing an increase of Garbage Collection overhead, impacting the performance of your application. The following code illustrates this:

   1: string s;
   2: s = "Hello"; //Stores "Hello"
   3: s += " how"; //Stores "Hello how"
   4: s += " are"; //Stores "Hello how are"
   5: s += " you?"; //Stores "Hello how are you?"
   6: Console.WriteLine(s);

When you run this code only the last string has reference. The other three are disposed of during Garbage Collection. These types of temporary strings should be avoided. Use the Concat, Join or Format methods of the String class to join multiple items in a single statement. You can also use the StringBuilder class to create a dynamic (mutable) string. The StringBuilder is the most flexible solution because it can span multiple statements. The default constructor allocates a 16 byte buffer and grows when needed. You can also specify it’s initial size and maximum size if you wish to do so. The following is a demonstration of the StringBuilder in use:

   1: System.Text.StringBuilder sb = new System.Text.StringBuilder(30);
   2: sb.Append("Hello"); //Builds the string.
   3: sb.Append(" how");
   4: sb.Append(" are");
   5: sb.Append(" you?");
   6: string s = sb.ToString(); //Copy result to string
   7: Console.WriteLine(s);

The String class overloads operators from System.Object Here is a list of the operators the String class overrides:

Operator C# Action on System.String
Addition + Joins two strings to create a new string.
Equality == Returns True if the two strings you are comparing have the same content and false if they are different.
Inequality != The inverse of the Equality operator.
Assignment = Copies the contents of one string into a new one. This causes the strings to behave a value types, even though they are implemented as reference types. This operator is called implicitly when you pass parameters by value.

Creating and Sorting Arrays

Arrays in C# are declared by using square braces (“[“ and “]”) as part of a variable declaration. System.Array provides members for working with its contained data. The following example creates an array and then sorts it:

   1: //Declare and initialize an array.
   2: int[] ar = { 3, 1, 2 };
   3: //Call shared/static array method.
   4: Array.Sort(ar);
   5: //Display the result.
   6: Console.WrteLine("{0}, {1}, {2}", ar[0], ar[1], ar[2]);

The output of the above code will be “1, 2, 3”, after having sorted the array which, without sorting would have resulted in an output of “3, 1, 2”.

How to Use Streams

Streams are a very common type. Streams are the means for reading from and writing to the disk and communicating across a network. The System.IO.Stream type is the base type for all task specific stream types. Network streams are found in the System.Network.Sockets namespace and encrypted streams are found in the System.Security.Cryptography namespace. The list below shows common stream types:

System.IO Type Use to:
FileStream Create a base stream used to write or to read from a file.
MemoryStream Create a base stream to be used to write to or read from memory.
StreamReader Read data from a text file.
StreamWriter Write data to a text file.

The simplest stream classes are StreamReader and StreamWriter, which enable you to read and write text files. A file name can be passed as part of the constructor allowing you to open a file with one line of code. After a file has been processed, call the Close method so that the file being processed does NOT remain locked. The following code is an example of how to read and write to a text file:

   1: //Create and write to a text file
   2: StreamWriter sw = new StreamWriter("text.txt");
   3: sw.WriteLine("Hello World");
   4: sw.Close();
   5:  
   6: //Read and display a text file
   7: StreamReader sr = new StreamReader("text.txt");
   8: Console.WriteLine(sr.ReadToEnd());
   9: sr.Close();

More will be discussed on streams in Chapter two. Stay tuned.

How to Throw and Catch Exceptions

Exceptions are unexpected events that interrupt the normal execution of an assembly. A simple example is if your assembly reads a file from a removable disk and while the application is reading the file the user removes the disk. The runtime will throw and exception. This makes sense because your assembly can not possibly read the data if the disk is removed and can not continue to run under those circumstances. Exceptions should never cause your assembly to fail completely. Instead a good programmer will plan ahead that exceptions will occur and catch them then respond to the events. In the preceding code example, if the file “text.txt” did not exist or was not available you could notify the user and wait for the user to respond. The following code is an example of this action:

   1: try{
   2:     //The C# "@" verbatim string literal before "C:\boot.ini" allows
   3:     //you to write the path as you would literally otherwise
   4:     //the forward slash "/" would be considered as being part
   5:     // of an escape sequence like "\t" for a tab etc.
   6:     StreamReader sr = new StreamReader(@"C:\boot.ini");
   7:     Console.WriteLine(sr.ReadToEnd());
   8: }
   9:catch(Exception ex){
  10:     //If there is any problem reading the file, display error message.
  11:     Console.WriteLine("Error reading file: " + ex.Message)
  12: }

In the preceding example any type of errors during reading of the file such as, “File not found” or “Insufficient privileges”  or any type of reading error during reading of the file, processing would continue in the “catch” block. If there are no errors at all, processing would bypass the catch block. The base Exception class contains an error message and other application data. The .NET Framework also has hundreds of exception classes to describe different types events all derived from System.SystemException. You can also define your own exceptions to describe an event in more detail that the standard Exception class will allow by deriving from System.ApplicationException.  You can have multiple exception classes to allow you to respond differently to different types of errors. The runtime executes only the FIRST catch block within a matching exception type, so order catch blocks from the most specific to the least specific. This is sometimes called  “Filtering Exceptions” The following is an example of “Filtering Exceptions”:

   1: try{
   2:     StreamReader sr = new StreamReader("text.txt");
   3:     Console.WriteLine(sr.ReadToEnd());
   4: }
   5: catch(System.IO.FileNotFoundException ex){
   6:     Console.WriteLine("The file could not be found.");
   7: }
   8: catch(System.UnauthorizedAccessException ex){
   9:     Console.WriteLine("You do not sufficient permissions.")
  10: }
  11: catch(Exception ex){
  12:     Console.WriteLine()("Error reading file: " + ex.Message)
  13: }

A “finally” block is also supported by Exception. The finally block runs after the try block and any catch blocks have finished executing, whether or not an exception  was thrown. You can use the finally block to close any streams or clean up any other objects that may be left open and could create an exception if left open. The following is an example of the “finally” block in use:

   1: StreamReader sr;
   2: try{
   3:     sr = new StreamReader(text.txt");
   4:     Console.WriteLine(sr.ReadToEnd());
   5: }
   6: catch(Exception ex){
   7:     //If there were any problems reading the file,
   8:     //display and error msg.
   9:     Console.WriteLine("Error reading file " + ex.Message);
  10: }
  11: finally{
  12:     //Close the StreamReader, Regardless of an error or not.
  13:     sr.Close();
  14: }

It is important to point out that the StreamReader declaration was moved outside the try block in the preceding example. This is necessary because the finally block can not access variables that are declared within the try block. This is so because depending on where the exception occurred, variable declarations within the try block may have not yet been been executed. TO catch exceptions both before and after the StreamReader declaration, use nested try/catch/finally blocks. Typically all code, except for simple variable declarations, should occur within try blocks. Exceptions that occur outside of the try blocks, or exceptions which occur within the try block and don’t have a catch block that matches their type, are passed up to the code that called the current method. If no higher level code matches the exception, the exception is considered unhandled and the CLR (Common Language Runtime) stops running the application.

The Exception.Message property provides a text message which describes the exception. Even though there are quite few users which can interpret the exception messages, many of which can be extremely complex, you should always use robust error handling and custom error messages for common scenarios.

Exception.StackTrace property is useful for debugging purposes because it includes the specific line number which initiated the exception. This information should NEVER be shown to the user, you should log the data in an event log for future troubleshooting purposes outside of the debugging environment.

Powered by Drupal - Design by artinet