|
2.
Types, Methods and Statements Lab |
|
Overview: you'll practice working with
.NET and the C# language by creating a console-based application in Visual
Studio .NET. You'll also experiment with some of the more subtle aspects
of OOP, namely testing for equality and sorting.
To earn a passing grade
for this lab, you must demonstrate both working programs to the instructor no
later than June 1.
Part 1: Visual
Studio .NET Project Setup
1.
Startup
Visual Studio .NET:
·
Start
menu
·
All
Programs
·
Microsoft
Visual Studio .NET 2003
·
Microsoft
Visual Studio .NET 2003
If this is the first
time, you may be presented with a start page asking you to select your profile
(VB programmer, C# programmer, etc.). Accept the default settings by
simply switching to the Projects tab (if available).
·
Click
File | New | Project (or click the New Project button)
2.
At
this point you should be presented with the New Project dialog. Select
"Visual C# Projects" as your Project Type, scroll down to select
"Console Application" as your Template using a single click. Set the Location by browsing to/creating a
new directory for this lab exercise on the H: drive. Then set the Name to
something creative like "LanguageApp".
You may see this message:

If so, simply click OK
and continue.
3.
Visual
Studio will create a main class called "Class1.cs" with an empty
static main method. Change the name of the class to MainClass,
and change the name of the file to "MainClass.cs"
(right-click on the icon for "Class1.cs" in the Solution Explorer
(top-right) window of Visual Studio, and rename to "MainClass.cs").
Notice the comments automatically inserted by Visual Studio. These comments have a special format that
allows a tool to generate web-based documentation.
4.
Let's
get comfortable with using Visual Studio .NET. Modify the main method to
simply output "Hello there!" to the screen --- wow, isn't
IntelliSense wonderful! You accept IntelliSense's suggestion by pressing
the TAB or ENTER key; you dismiss IntelliSense by pressing ESC.
5.
To
compile a program, use the Build menu and select the Rebuild option for best
results. To run, press F5. Go ahead and press F5. The program
will run, a command window will open, your message will flash on the screen, and
then the command window will close since the program has ended. Normally
we want to keep the command window open so we have time to actually see the
output :-) A common trick is to wait for the user to enter something on
the keyboard, i.e., a call to System.Console.ReadLine(). Add this to the end of your main
subroutine and run again; now the command window remains open until you press
ENTER
6.
While
we're getting setup with Visual Studio, now is a good time to make the fonts
bigger so it's easier for you to work with VS. Under the Tools menu,
select Options, and then under the list for Environment, select Fonts and
Colors. Show the Settings for Text Editor (this should be the default
that appears), and set the font to say 12pt. Then show the settings for
the Text Output Tool Windows, and set that to 12pt. Finally, in the list
that appears in the left pane, select Text Editor, then select All Languages,
then select Tabs. Set the Tab and Indent Size to 4, and select the
"Keep tabs" option. Click OK. Visual Studio is now
reasonably configured.
7.
Okay,
you're ready to start programming!
Part 2: Classes,
Arrays and Namespaces

1.
Above
is a screenshot of the application you will build. The idea is to input some
employee information from the user, collect them in an array of Employee
objects, and then manipulate and output the data. Note that the
application is dynamic, in that the user determines how many employees will be
input --- 3 in the example above --- and the application reacts
accordingly. We'll build the application slowly, step by step...
2.
The
first step is to add an Employee class to your project:
·
Click
Project
·
Select
Add Class
·
Set
file name to "Employee.cs".
An employee has 4
fields: first name, last name, salary and email address. The names
and email address are strings; the salary is a decimal. Each of these
attributes should be private.
Implement a
parameterized constructor with four arguments to set up the initial values of
the employee’s fields.
Override ToString()
to return the employee's full name in the format “last name, first name”. Don’t forget the keyword “override”.
3.
Back
in the
4.
In
the
5.
Write
a private method called GetEmployees(int N) that returns
an array of N employee objects:
The method should create an Employee
array of size N
Prompt the user for information for
each of N employees. Read the data for each
employee from the user at the keyboard and instantiate a new Employee.
Place each Employee object into the
array, then return a reference to the array object
back to the caller.
When inputting the salary, note that
.NET only allows you to input either a character, or the entire line (as a
string). The best approach in this case is to input the entire line using
System.Console.ReadLine(), and then type convert the input string to
an decimal. Use Intellisense
to figure out which method in the Convert class should be used to do this.
6.
Back
in the Main method, call GetEmployees() to input the employee data and return an array of
employee objects. Save the returned array reference in the local
variable, emp, of type Employee[].
7.
Immediately
loop through the array and output each employee to the screen. Use the foreach loop to iterate through the array, calling ToString()
on each employee object and outputting the result.
8.
Compile,
run and test. At this point you should be able to run, input some employees,
and print them back out --- as shown in the earlier screenshot.
Obviously, keep N small :-)
9.
Notice
that Visual Studio automatically imports the System namespace by putting
using System at the top of each source file. This means you can
drop the System prefix if you prefer and write Console.WriteLine(...)
and Console.ReadLine(). Try it if
you haven't already.
On the other hand, note
that your Employee class is declared within a namespace (LanguageApp).
This implies that unless you import the namespace, you must refer to the
Employee class as LanguageApp.Employee.
But you probably referred to the class as Employee. So why did it
work?
Simple: The main
class is part of the same namespace, so it's perfectly legal for the main class
methods to refer to the class simply as Employee. But go ahead and try
the fully-qualified name LanguageApp.Employee -- that
should work as well.
10.
DEMONSTRATE THIS SOLUTION NOW OR SAVE A COPY OF THIS
VERSION AND DEMONSTRATE IT LATER.
11.
Good
work!
Part 3: Searching

1.
The
next step is to let the user search for an employee in the array, and if found,
output that employee's salary.
2.
Add
a GetSalary() method to the Employee class that
returns the employee's salary as a nicely-formatted string like this:
public
string GetSalary() {
return this.salary.ToString("$#,##0.00");
}
3.
Modify
the Main method so that after outputting the employees to the screen, you input
an employee's first and last name from the user. The modern, object-oriented
way to compare Employees is to compare the objects themselves, not the data
fields. Modify the Main method so that after inputting the employee's
first and last name from the user, you create a
Employee object theE with a dummy salary and email
address. Write a loop to iterate through the employees array, and for
each employee object in the array use the = = operator to compare the employee
in the array to employee theE using the = =
operator. If a match is found output the employee's salary; if no match
is found, inform the user that the search failed.
4.
Run
and test. To make testing easier, feel free to hard-code the value of N
in main, and hard-code the GetEmployees method to
return an array of predetermined Employee objects. Hint:
A decimal type literal ends with ‘m’.
For example: 100.92m
5.
Using
this approach, no matter what the user inputs, the program will never find a
match. As discussed in class, this is because the = = operator compares
object references, not object data.
6.
Okay,
being good OO programmers, rewrite the search loop so that for each employee in
the array, you call Equals(theE)
to compare the objects properly. Run and test, and once again, no matter
what the user inputs, the program will never find a match.
7.
The
problem is that the Employee class does not fulfill its half of the
responsibility by overriding the Equals method. Modify the Employee class
to override Equals, where two employees are equal if they have the same first
and last names. Now compile, run and test. The search should now
work properly.
8.
One
last thing: if you look VERY carefully at the output messages when you
compiled the program (you'll have to scroll the output window), you'll notice a
warning saying that you didn't override GetHashCode(). Likewise, you'll see a squiggly
under the class name Employee. While you are free to ignore the warning,
I don't recommend it So let's properly override GetHashCode() as well.
9.
Here's
the rule for implementing GetHashCode(): if obj1.Equals(obj2), then obj1.GetHashCode() ==
obj2.GetHashCode(). In other words, if two objects are equal by your
definition of Equals, then those same objects must hash to the same integer
code. If the two objects are different by your definition of Equals, then
it doesn't matter what GetHashCode returns (though
the hope is that you'll return different hash codes).
10.
Okay,
so go ahead and override GetHashCode() in the Employee class. Since two employees are
equal if their names are equal, a reasonable hashing scheme that adheres to the
rule is one in which we add together the individual hash codes for the first
and last names. As a result, if two objects have the same first and last
names, then those objects will also have the same hash code. Compile, run
and test. You should notice no difference, other than the warning is now
gone. But even though you don't notice a difference today, forgetting to
override GetHashCode() will cause a problem someday...
11.
DEMONSTRATE THIS SOLUTION NOW OR SAVE A COPY OF THIS
VERSION AND DEMONSTRATE IT LATER. Excellent!
Part 4: If time
permits and you want to experiment further...
A. Sorting, Min, Max and Median (OPTIONAL)
1.
The
goal is to output the min, max and median salary values for the employees input
by the user.
2.
Given
a sorted sequence of numeric values, the median is the value in the
middle. If the sequence consists of an odd number of values, the median
is literally the middle value. If the sequence consists of an even number
of values, the median is the average of the two middle values. Either
way, we need to sort the values first...
3.
The
good news is that .NET contains a sort routine that you can simply call, and it
will do the work of sorting the values for you. The subroutine is named
Sort, and it is a static method residing in the System.Array
class. Pass your array to this subroutine, and
it will come back sorted. The good news is that System.Array.Sort(array)
can sort an array of anything. The bad news is that the objects in the
array must implement a particular .NET interface (i.e., a set of
methods) in order for Sort to work. Since our Employee class does not
implement the required set of methods, we cannot sort our employees array just
yet. However, the System.Int32 class does implement the required
set of methods, so the solution is to create a separate array of type int[],
copy the employee salaries over into that array (converting to type int), and sort.
4.
Go
ahead and modify your main method so that after it performs the search, it
sorts the salaries via a separate int[] array and then for now outputs the min and max
salaries. Run and test.
5.
Let's
compute and output the median. Assume for a moment that the user will
enter an odd number of employees. In this case the median is easy:
it's the middle element of the sorted sequence. Given an array with N
elements, the index of the middle element is typically computed by dividing N
by 2 and storing the result in an integer variable. However, you always
have to consider the question of rounding. Dividing an odd number by 2
yields a .5 --- does C# round-up, round-down, or both (i.e.,
alternating)? We need C# to round-down, for example 5 / 2 needs to be 2
(2 is the middle of an array with 5 elements 0 .. 4), and 7 / 2 needs to be 3
(3 is the middle of an array with 7 elements 0 .. 6), and so on. You
could write some test code to see what C# does, or you could just make sure it
works properly by using the FxCL..
6.
In
mathematical terms, we want the floor of N / 2, i.e., "the largest
whole number less than or equal to the result." Thus, to properly
compute the middle index and guarantee that we round-down, we can use the System.Math.Floor(result) method as follows:
int mid;
mid = (int)
System.Math.Floor(N / 2.0);
We divide by 2.0 to force real
division, then we ask the FxCL
to round-down for us. Based on this value of mid, you can now access and
output the median final salary (we are still assuming N is odd). Compile,
run and test with odd values for N. Convince yourself that you are
outputting the correct median value.
7.
You
may be wondering: Do we really need to use System.Math.Floor?
Turns out you don't need it here in C#, but you do in VB. The underlying
point here is that you need to program defensively if you don't already.
Switching between languages is so common these days that it's dangerous to rely
upon language-specific details: rounding, operator precedence,
short-circuiting, etc. Instead, you need to focus on the concepts, and
develop general, language-independent techniques for implementing those
concepts.
8.
The
last step is to handle the case where N is even. In this case, the median
is the average of the middle two elements. For example, given an array
with N = 8 elements (0 .. 7), then mid = 4, and thus
you want to take the average of elements #3 and #4. That's easy enough,
as long as you remember to divide by 2.0 to force real division, and store the
result in a variable of type double. The only tricky part is
determining whether N is even or odd. A common trick is to use the
"mod" or "remainder" operator, which in C# is %. The
result of N%K is the integer remainder of dividing N by K. Thus, if N%2
is 0 then N is even, else N is odd. Here's the code:
if (N % 2 == 0) // no
remainder means N is even!
System.Console.WriteLine("N is even!");
else
System.Console.WriteLine("N is odd!");
Using this code, compute and output
the median value properly whether N is even or odd.
B. Formatting, Checking Inputs, Implementing an
Interface (OPTIONAL)
1.
When
outputting real numbers (e.g., doubles) to the screen, by default .NET will not
print the 0's to the right of the decimal point. For example, the real
number 12.0 is output as 12, not 12.0. Formatting is controlled via the ToString()
function by supplying a format string. In the case of real numbers, if
you convert the variable to a string using the format string "0.00",
then you're telling .NET to always print at least 1 digit to the left of the
decimal point, and 2 digits to the right --- whether those digits are 0 or
not. Here's an example:
double median;
string s;
median = ...
s = median.ToString("0.00");
Modify your program to output the
median using a format string like the one above, and test.
2.
What
if the user enters 0 for the number of employees? Or a negative
value? What does your program do? Handle this situation by either
stopping the program at this point (by returning from the main method or
calling System.Environment.Exit(1)), or by repeatedly inputting an integer
from the user until he/she enters a value > 0.
3.
The
System.Array class contains an IndexOf(Array a, object obj) method for searching a given array for a given
object. This generic, linear search method operates by comparing obj to each object in array a; comparison is performed by
calling obj's Equals()
method. If obj is found then its index in the
array is returned, otherwise a negative number is returned. Extend your
main program to also search based on this IndexOf() method, and confirm that IndexOf()
reports the same findings as your search loop.
4.
It
would be more efficient, and more useful, if we could sort the array of
employees directly. This would save us from having to create a second
array, and from having to copy all the values over to the second array.
Also, as it stands right now, there's no way to associate the sorted salaries
with the employees. In particular,
there's no easy way to determine the employee with the max salary, the min
salary, or the median. The solution is to make the Employee objects sortable by implementing the method(s) defined in the .NET
interface IComparable. The employees
should order themselves based on their salary. Note that we haven't
talked about interfaces yet, so if interfaces are new to you, you might want to
wait on this one :-)
5.
Okay,
that's plenty. Take a well-deserved break!