Lab One: General DotNet and C# Concepts

Overview:

In this lab you will familiarize yourself with the Visual Studio IDE and write a library that is consumed by an application. Along the way you will exercise your C# skills as the language will be used extensively in the rest of the course.

Goals:

§  Learn your way around the Visual Studio IDE

§  Create a class library

§  Create an application that uses the class library

Part 1: Creating a Library Project

In this part of the lab you will add a class library project to the existing solution. This library will process text and calculate metrics on the text.

  1. Open Visual Studio and create an empty solution—name it DotNetLab1. Copy the text file “hamlet.txt” to the empty solution folder. This text file contains Hamlet’s soliloquy from the Shakespeare play. This is the sample text that we will be using to test the application
  2. Right click on the solution and add a new project of type Class Library. Call the class library TextUtils. A class library project generates a DLL rather than an EXE – in .NET the only difference between the two is an EXE must have an execution entry point (called Main)
  3. Rename the generated file from Class1.cs to TextCounter.cs. You will be asked if you want to rename the type as well – say yes as this will rename the class from Class1 to TextCounter.
  4. The TextCounter class is a utility class for calculating various metrics from a piece of text. Mark the class as a static class (using the static keyword) to tell the compiler that the class will only have static members. If you accidently add a non-static member you will receive a compiler error

public static class TextCounter

{

}

  1. Add a public static method called CountCharacters which takes a string as a parameter and returns an int. In the method body simply return the Length property of the passed string

public static int CountCharacters(string text)

{

return text.Length;

}

  1. We have created a bare bones class library that we will enhance later. However, to be able to run the code we will need an application that we can execute. You will create this application in the next part

Part 2: Creating an Application that uses the Library

In the second part of the lab you will create a console application that uses the functionality of the class library you just created by passing the text contained in the hamlet.txt file

  1. Right click on the solution and add a new project of type Console Application. Call this project Architecture.
  1. Right click on the Architecture project and select Add Reference. Select the Projects tab and add a reference to the TextUtils project. This allows us to use the types in TextUtils from our Architecture application
  2. In program.cs, in Main, add a local variable of type StreamReader called reader and initialize it to a new instance of a StreamReader. You will have to pass a constructor parameter specifying the file to open to the StreamReader. Use a relative path to the Hamlet.txt file. Backslashes are generally used to indicate escape sequences in strings in C# so you need to use what is known as a literal string by putting an @ sign before the string.
  3. If you try to compile the project you will notice that the compiler doesn’t know about the StreamReader class. This is because it is in the System.IO namespace. You need to add a using statement for System.IO at the top of the file. You can either do this explicitly or put the cursor on the StreamReader class name in the editor and press CTRL+. – this will drop the smart-tag menu which has an option to add a using statement. Having added the using statement the code should now compile.

static void Main(string[] args)

{

StreamReader reader = new StreamReader(@"..\..\..\hamlet.txt");

}

  1. Try to run your application by pressing CTRL-F5. You will get an error stating that a project of type class library cannot be run. If you look in Solution Explorer you will notice that the TextUtils project is in bold text which means its set as the active project (the one that will be run by default). You need to change this to the Architecture project by right clicking on the Architecture project and selecting Set as StartUp Project. You should now be able to run the console application using CTRL-F5. You won’t see any interesting output but the program should execute without throwing an exception. This means you have entered the correct path to the Hamlet.txt file
  2. As good practice you should close the reader at the end of Main so add a call to the Close method of the reader
  3. We need to retrieve the text from the file: add a local variable of type string, called text, after creating the reader, but before closing it. Initialize text by calling the ReadToEnd method of the reader

static void Main(string[] args)

{

StreamReader reader = new StreamReader(@"..\..\..\hamlet.txt");

string text = reader.ReadToEnd();

reader.Close();

}

  1. Now, after getting the text from the file, call the CountCharacters static member of the TextCounter static class. Remember to call static members you use the type name rather than create an instance. Also, for this code to compile you will need to add a using statement for the TextUtils namespace at the top of the file. Write the number of characters to the console window.

static void Main(string[] args)

{

StreamReader reader = new StreamReader(@"..\..\..\hamlet.txt");

string text = reader.ReadToEnd();

Console.WriteLine("The number of characters is: {0}",

TextCounter.CountCharacters(text));

reader.Close();

}

  1. Compile and run your code. You have now successfully used a custom library from an application

Part 3: Add Further Functionality to the Library

In this part of the lab you will add further methods to the TextUtil library to count the number of words, instances of a word and the average word length

  1. Go to the TextCounter class in TextCounter.cs. Add a public static method called CountWords to the class that returns an int and takes a string for the text to check
  2. You are going to use the Split method of the string class that breaks the string up into an array of sub-strings based on supplied delimiters. We need to add delimiters for everything that could not be part of a word. Add a const string called WhiteSpace to the static class. Give it a value of " ,.?!;:\"-\n\r". The \”, \n and \r are escape sequences for a quote, new line and carriage return respectively
  3. The Split method on the string class takes a character array rather than a string. Create a local variable in the CountWords method of type char array and initialize it by calling the ToCharArray method on the WhiteSpace string
  4. Create a local variable of type string array called words and initialize it by calling Split on the text parameter passing the character array you just created. This string array has each of the words from the text in it
  5. Return the Length of the word array to the caller
  6. You are going to be using the functionality of getting the words from the text a number of times so it makes sense for this to be a separate helper method. Highlight the two lines that get the number of words and then, using the Edit menu option, select Refactor->Extract Method and call the method CountWords. Notice that the generated method is marked as private meaning it can only be called from inside the TextCounter class. Refactoring is a very useful feature of C#

public static int CountWords(string text)

{

string[] words = GetWords(text);

return words.Length;

}

private static string[] GetWords(string text)

{

char[] whitespaceChars = WhiteSpace.ToCharArray();

string[] words = text.Split(whitespaceChars);

return words;

}

  1. Add another new public static method to the TextCount class called GetAverageWordLength that takes the text as a string and returns a double for the average length.
  2. Get the words from the text using the GetWords helper method. We now need to count all of the characters in all the words. The simplest way to do this is to concatenate all of the words into another string (now without any whitespace) and find the length of this string. To do this use the static Concat member of string and pass it the string array.
  3. Finally return the total number of characters in the words divided by the number of words (found from the Length of the word array). You will need to cast both the array lengths to double otherwise you will perform integer arithmetic rather than floating point.

public static double GetAverageWordLength(string text)

{

string[] words = GetWords(text);

string wordCharacters = String.Concat(words);

return (double)wordCharacters.Length / (double)words.Length;

}

  1. Add one last public static method to the TextCounter class to find the number of instances of a supplied word in some text. Call this method CountMatchingWords and pass it the text and the target word as strings. Return the matching word count from the method
  2. In the method body get the number of words in the text using the GetWords helper method
  3. Create a local variable, of type int, to maintain the count of matching words – initialize this to 0
  4. Iterate through the array of words testing each one to see whether it is a match with the target word. Use a foreach loop to perform the iteration. If the current words matches the target word then increment your count
  5. Return the count at the end of the method

public static int CountMatchingWords(string text, string targetWord)

{

string[] words = GetWords(text);

int matchCount = 0;

foreach (string word in words)

{

if (word == targetWord)

{

matchCount++;

}

}

return matchCount;

}

  1. Go to Main in the Architecture project and call each of these new methods printing the results to the console. Use “the” for the target word in the call to CountMatchingWords
  2. Compile and test your code

Part 4: Encapsulate the Text Processing in a Class

In this part of the lab you will create a class that analyses the text and generates metrics (based on the static methods you have already created). In the process you will hide the static class from code outside the library providing the class as the sole interface into the functionality

  1. Add a new class to the TextUtils project called TextStatistics and mark the class as public (we want to be able to access this from the Architecture project)
  2. Add a constructor that takes the text as a string
  3. Add four readonly automatic properties (the propg snippet will help here)

Name / Type
WordCount / int
CharacterCount / int
InstancesOfThe / int
AverageWordLength / double

public class TextStatistics

{

public TextStatistics(string text)

{

}

public int WordCount { get; private set; }

public int CharacterCount { get; private set; }

public int InstancesOfThe { get; private set; }

public double AverageWordLength { get; private set; }

}

  1. Initialize each of these properties in the constructor by using the static methods on the TextCounter class

public TextStatistics(string text)

{

WordCount = TextCounter.CountWords(text);

CharacterCount = TextCounter.CountCharacters(text);

InstancesOfThe = TextCounter.CountMatchingWords(text, "the");

AverageWordLength = TextCounter.GetAverageWordLength(text);

}

  1. We now want to prevent the TextCounter static methods being used outside of the library. Go to the class definition and mark it as internal rather than public
  2. If you try to compile now you will get failures in Main stating the member is inaccessible due to its protection level. Rewrite Main to create an instance of the TextStatistics class and then change the Console.WriteLine calls to print out the appropriate properties of the TextStatistics object

TextStatistics stats = new TextStatistics(text);

Console.WriteLine("Number of words is: {0}", stats.WordCount);

Console.WriteLine("Number of characters is: {0}", stats.CharacterCount);

Console.WriteLine("Instances of the: {0}", stats.InstancesOfThe);

Console.WriteLine("Average word length: {0}", stats.AverageWordLength);

32.  Compile and test your code