Python is a convenient and fun programming language, but its ease of use makes it easy for others with bad intentions to break its programs. So, Python is not always the first choice for building programs that will be sold to and used by the general public.
C# (``C sharp'') is a programming language developed by Microsoft for secure programming. C# is closely related to C++ and Java, and it makes many small improvements on these two languages. In addition, Microsoft has written a huge, useful library of C# program modules and classes called .NET --- by learning C# you will be able to use .NET. There is also a powerful IDE for C# called Visual Studio, which you will learn to use in a later course.
C# contains many protection mechanisms to hinder abuse and is a good choice for ``industrial strength'' computer programming. On the negative side, C#'s protection mechanisms cause the language to be verbose (there are lots more keywords and brackets than in Python) and clumsy (nice data structures like tuples, dictionaries are not available). But for practical reasons, it is important to learn how to adapt one's Python programming skills to C#.
There is an important conceptual difference between Python and C#: Python is a ``write and test'' programming language --- a scripting language, where a person types commands and tries them immediately. This immediate testing is done with the aid of the Python interpreter, which rests in computer storage with the Python program and mediates between the program and the computer's processor.
In contrast, C# is a ``check then execute'' language --- a batch language --- where the entire C# program must first be inspected and checked by the C# compiler program. If the compiler finds no grammatical errors in the C# program, then the compiler generates a translated version of the program as an .exe-file, which can then be executed by itself, working directly with the processor.
The check-then-execute style does well at spotting errors in advance of using the program. Also, since an interpreter program is not needed to use the translated (compiled) C# program, the latter can be embedded into computer chips in appliances like toasters, elevators, cars, and airplanes, and pacemakers.
A one-module Python
program can be adapted without too much pain into a C# program.
Figure 1 gives an example, which prints a greeting to a person
after they type their name. First, here is the Python coding:
=================================
name = raw_input("Please type your name: ")
print "Hello there, " + name + "!"
======================================
And here is the C# coding:
FIGURE 1======================================
using System;
/// <remarks>Hello demonstrates input and output
/// assumed input: a person's name
/// guaranteed output: a personalized greeting, sent to the command
/// window and a dialog </remarks>
public class Hello
{ public static void Main()
{ Console.Write("Please type your name: ");
string input = Console.ReadLine();
Console.WriteLine("Hello there," + input + "!");
}
}
ENDFIGURE============================================================
Here are points to remember:
using System; public class PROGRAM_NAME { public static void Main() { ... your program goes here ... } }because every C# program must be placed in a class. The name of the class should match the name of the file where the class lives. (Here, we place class Hello in a text file named Hello.cs.)
Console.Write(3);If you wish to print a string and an int on the same line, you can do so like this:
Console.Write("Two plus one equals "); Console.WriteLine(3);Or like this:
Console.WriteLine("Two plus one equals " + 3);(The + represents both numerical addition as well as string concatenation. The latter applies here.)
Or like this:
Console.WriteLine("Two plus one equals " + 3.ToString());
(The ToString method converts the integer, 3, into a string
so that it can be concatenated to the text that precedes it.)
If a number must be printed some fixed number of places,
a format string can be used with ToString. For example,
Console.WriteLine("Two plus one equals " + 3.ToString("000"));
will print
Two plus one equals 003.
Console.WriteLine("hello"); // writes a string to the command window
Console.Write("Please type your name: "); /* DON'T DO THIS JUST YET: string input = Console.ReadLine(); Console.WriteLine("Hello there," + input + "!"); */
string input = Console.ReadLine();declares that variable input is a cell that holds only strings. (Other such data types are int, float, double, char, and boolean.)
If we would write
string input = 2; or int input = "howdy";
these would be errors, and the program would not execute.
After its declaration, a variable can be used just as in Python,
e.g.,
input = input + " and here is more text";
but the variable's data type must be respected, so for example,
this would be an error for the string-variable, input:
input = 3;
Say that you saved the above C# program in the file,
Hello.cs.
First, you must compile the program (check spelling, grammar, data
types, and translate
into an exe-file). Do this by opening a command
window, opening the folder that holds Hello.cs, and typing
at the command line,
csc /debug+ Hello.cs
If you make a spelling or punctuation
error, you will be informed; read the error message, note the line and
character where the error appears,
correct it, and compile again.
If the program is properly spelled, then you will receive a prompt,
and a file, Hello.exe, will be constructed in the same
folder where Hello.cs lives.
You type next
Hello
This executes the program directly, without assistance of an interpreter or
helper modules --- the program operates all alone.
If your program contains an indexing or some other execution error, the program will halt. Because you included the /debug+ option when you compiled the program, the compiler has inserted instructions that will tell you the line where the execution error occurred. (If you had typed merely, csc Hello.cs, then an execution error causes the program to stop with no information about the source of the error.)
If you type csc /debug+ Hello.cs in the command window,
and you see
'csc' is not recognized as an internal or external command,
operable program or batch file
then it means that you must help the command window locate
the C# compiler, csc. Here's how:
You will probably locate the program in a folder named something like
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
This folder is the ``path'' to the compiler.
set path=c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727Now, you can cd to the folder that contains your C# program and type csc /debug+ Hello.cs
;c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
pathYou will see the list of folders (paths) that the command window searches when you ask it to execute a program (like notepad or csc). The last folder in the list should be the path to the C# compiler that you just added. You can also check by typing just
cscand you will get a message from the C# compiler.
In addition, your computer might already have an IDE (Integrated Development Environment) for C# (e.g., Visual Studio). To find out if you do, open a file window and locate the icon for Hello.cs. Double-click on it. If a complex IDE window appears, and your program appears in the IDE's window, then you can compile and execute the program by clicking on the buttons or menu items of the IDE --- Look for a button named Compile or Build to compile the program. Then, look for a button named Execute.
C# programs can compute on numbers, just like Python.
Here is a small program that reads an integer and computes
its fractional reciprocal:
==============================================
using System;
///
Here are some important points:
int n = Int32.Parse(input);This calls a helper method, Parse, within module Int32 within module System, to convert the sequence of symbols into a computer integer of 32 bits (one fullword).
int n = Int32.Parse(input);or
int zero = 0;These assignment statements use the data-type information to validate that the correct form of value is assigned to the variable.
When the variable is referenced or assigned later, its data type
need not be repeated, e.g.,
zero = zero * 4;
is acceptable, because we already know that variable
zero holds an integer value.
double one = 1.0;or
double answer = 1.0 / n;
bool OK = true; char letter = 'a':
Here is a second arithmetic example:
the MakeChange3.py
program that appeared in Chapter 4 of these notes, coded
in Python:
==============================
# MakeChange3
# calculates change in coins for a dollars, cents amount
dollars = int(raw_input("Please type dollars amount: "))
cents = int(raw_input("Please type a cents amount: "))
if not ((dollars >= 0) and (cents >= 0) and (cents <= 99)) :
print "Error"
else :
money = (dollars * 100) + cents
print "Quarters =", (money / 25 )
money = money % 25
print "Dimes =", (money / 10)
money = money % 10
print "Nickels =", (money / 5)
money = money % 5
print "Pennies =", money
============================================
Here is the C# coding, placed in a file named MakeChange.cs:
FIGURE 2===================================================
using System;
///<remarks>MakeChange
/// calculates change in coins for a dollars, cents amount.
/// assumed input: two numbers ---
/// a dollars amount, a nonnegative integer;
/// a cents amount, an integer between 0 and 99.
/// guaranteed output: the coins for the amount </remarks>
public class MakeChange
{ public static void Main()
{ Console.Write("Please type dollars amount: ");
string input = Console.ReadLine();
int dollars = Int32.Parse(input); // convert input into an int
Console.Write("Please type cents amount: ");
int cents = Int32.Parse(Console.ReadLine());
if ( !((dollars >= 0) && (cents >= 0) && (cents <= 99)) )
{ Console.WriteLine("Error"); }
else {
int money = (dollars * 100) + cents;
Console.WriteLine("Quarters = " + (money / 25));
money = money % 25;
Console.WriteLine("Dimes = " + (money / 10));
money = money % 10;
Console.WriteLine("Nickels = " + (money / 5));
money = money % 5;
Console.WriteLine("Pennies = " + money);
}
}
}
ENDFIGURE=====================================================
This program uses a conditional command.
if ( TEST1 ) { COMMAND1; } else if ( TEST2 ) { COMMAND2; } else if (TEST3 ) { ... } else { LASTCOMMAND; }
Say that you saved the above C# program into the file,
MakeChange.cs.
As before,
you must compile the program (check spelling, grammar, and translate
into an intermediate format). Do this by opening a command
window, opening the folder that holds MakeChange.cs, and typing
at the command line,
csc /debug+ MakeChange.cs
If you make a spelling or punctuation
error, you will be informed; read the error message,
correct the error, and try to compile again.
If the program is properly spelled, then you will receive a prompt,
and you can type next
MakeChange
This executes the program, which behaves the same as the Python version.
You can generate random numbers in C# just as easily as done in Python;
do it like this:
Random r = new Random(); // starts a random-number generator and names it r
...
int num = r.Next(6); // assigns to num a random number in the range 0..5
You can use r.Next(...) over and over to generate random
numbers.
===================================== # PrintChars # prints a string's characters from left to right, one at a time: text = raw_input("Type a string: ") index = 0 # remembers which character we are looking at in text while index < len(text) : # invariant: we have printed text[0], text[1], ..., up to text[index-1] print text[index] index = index + 1 raw_input("\n\npress Enter to finish") ENDFIGURE=============================
Here is how it looks in C#:
FIGURE 3================================================
using System;
///<remarks> PrintChars0
/// prints a string's characters from left to right, one at a time </remarks>
public class PrintChars0
{ public static void Main()
{
Console.Write("Please type a string: ");
string text = Console.ReadLine();
int index = 0; // remembers which character we are looking at in text
while ( index < text.Length ) {
// invariant: we have printed text[0], text[1], ..., to text[index-1]
Console.Write(text[index]);
index = index + 1;
}
}
}
ENDFIGURE=====================================================
The only novelty is that we use text.Length to obtain the integer length of string text.
Like its ancestor languages, C++ and Java, C# includes a counting
form of for-loop. Here is the above program, reprogrammed with
C#'s for-loop:
FIGURE 4===================================================================
using System;
public class PrintChars1
{ public static void Main()
{ Console.Write("Please type a string: ");
string text = Console.ReadLine();
for ( int index = 0; index < text.Length; index = index + 1 )
{ char c = text[index];
Console.Write(c);
}
}
}
=========================================================================
With the C# for-loop, you must declare a variable, i, that counts from
0 to the length of string text and extract its characters
one at a time:
for for ( int i = 0; i < text.length(); i = i + 1 ):
Also, note that a single letter is called a char in C#.
The C# for-loop proves useful when we must process the elements
of a structure in a nonstandard order. For example, we print backwards
the letters within string text this simply:
for ( int i = text.Length; i >= 0; i = i-1 )
{ Console.Write(text[i]); }
Finally, the Python for-loop is called foreach in
C#. Here is the above example, one more time:
FIGURE 5=====================================================
using System;
public class PrintChars2
{ public static void Main()
{ Console.Write("Please type a string: ");
string text = Console.ReadLine();
foreach (char letter in text)
{ Console.Write(letter); }
}
}
======================================================
Notice that the auxiliary variable, letter, must
be declared with its data type, char.
A standard example is alphabetizing (sorting) a string.
Here is the program from Figure 6 of Chapter 4:
=====================================================
# Sort alphabetizes a string.
# assumed input: s - a string
# guaranteed output: output - the characters in s arranged alphabetically
s = raw_input("Type word to sort: ")
output = ""
for letter in s :
# invariant: at the i-th iteration,
# output holds the first i-1 letters in s, alphabetized
# Find the position in output where we should insert letter:
index = 0
for alpha in output :
if letter < alpha :
break
else :
index = index + 1
# Insert letter at position index in output:
output = output[:index] + letter + output[index:]
print output
=====================================================
The program makes clever use of Python's for-loop and
substring functions.
We can repeat the strategy in C#:
FIGURE 6===============================================
using System;
/// <remarks> Sort alphabetizes a string.
/// assumed input: s - a string.
/// guaranteed output: output - the characters in s,
/// arranged alphabetically. </remarks>
public class Sort
{ public static void Main()
{
Console.Write("Type word to sort: ");
string word = Console.ReadLine();
String output = "";
foreach (char letter in word)
// invariant: at the i-th iteration,
// output holds the first i-1 letters in word, alphabetized
{
int index = 0;
// Find the position in output where we should insert letter:
foreach (char alpha in output)
{
if ( letter < alpha )
{ break; }
else
{ index = index + 1; }
}
// Insert letter at position index in output:
output =
output.Substring(0,index) + letter + output.Substring(index);
}
Console.WriteLine(output);
}
}
ENDFIGURE=====================================================
The example program shows that we can use break to exit a loop. Also, there are the two important methods for building substrings of a string:
(Note: Fortunately, the .NET library contains some C#-written classes that simulate lists and dictionaries. We won't study these here but focus instead on the C# core language. You will study the .NET versions in the next course.)
Before using an array, one must state the data type of elements the array holds
and the length of the array. Repeating the starting example
in Chapter 5, here is a 3-element array of strings that
describes the contents of my clothes chest. The array is constructed
and its address is assigned to the variable, chest:
string[] chest = new string[3];
chest[0] = "3 shirts";
chest[1] = "2 pants";
chest[2] = "no socks";
The first command, the declaration, is important:
To print the contents, write a foreach-loop
that enumerates the array's elements:
foreach ( string s in chest )
{ Console.WriteLine(s); }
Here is a more elaborate printout:
for( int i = 0; i != chest.Length; i = i + 1 )
{ Console.WriteLine("drawer " + i + " holds " + chest[i]); }
Here is a more complete example. Recall the vote-counting
example from Chapter 5, where the votes for 4 candidates
(Candidate 0, 1, 2, and 3) were tallied by a loop program, which
updates a four-celled list.
The Python coding was this:
================================================
votes = 4 * [0] # holds the votes for the 4 candidates
# collect the votes:
processing = True
while processing
v = int(raw_input("Type your vote (0,1,2,3): ")
if v < 0 or v > 3 :
processing = False # bad vote, so quit
else :
votes[v] = votes[v] + 1
# print the totals:
count = 0
while count != len(votes) :
print "Candidate", count, "has", votes[count], "votes"
count = count + 1
========================================================
Here is the corresponding C# version:
=========================================================
using System;
public class Votes {
public static void Main() {
int[] votes = {0, 0, 0, 0}; // holds the votes for the 4 candidates
bool processing = true;
// collect the votes:
while ( processing )
{ Console.Write("Type your vote (0,1,2,3): ");
int v = Int32.Parse(Console.ReadLine());
if ((v < 0) || (v > 3))
{ processing = false; } // bad vote, so quit
else
{ votes[v] = votes[v] + 1; }
}
// print the totals:
for( int i = 0; i != votes.Length; i = i + 1 )
{ Console.WriteLine("Candidate " + i + " has " + votes[i] + " votes"); }
}
}
=========================================================
C# arrays can be grids, too. Recall the slide-puzzle game
from Lecture 5 --- it used a 4-by-4 grid of integers and was
constructed like this in Python:
puzzle = [ [15, 14, 13, 12],
[11, 10, 9, 8],
[ 7, 6, 5, 4],
[ 3, 2, 1, 0] ]
We have two ways of building the grid in C#. The first is called
a two-dimensional array:
int[,] puzzle = { {15, 14, 13, 12}, // use braces, not square brackets,
{11, 10, 9, 8}, // to initialize an array
{ 7, 6, 5, 4},
{ 3, 2, 1, 0} };
We index the array with two integers, separated by a comma, e.g.,
puzzle[1,3] refers to the element in row 1, column 3: 8.
Here is the Python coding of the slide-puzzle program, followed
by its C# version:
=============================================
# SlidePuzzle implements a slide-puzzle game.
PUZZLE_SIZE = 4
# the puzzle, where 0 marks the empty space/cell:
puzzle = [ [15, 14, 13, 12],
[11, 10, 9, 8],
[ 7, 6, 5, 4],
[ 3, 2, 1, 0] ]
# it is convenient for us to remember which cell is empty:
empty_space = (3, 3)
# loop forever to move the pieces as dictated by the user:
while True :
# print the puzzle:
for row in puzzle :
print row
# ask for the next move:
num = int(raw_input("\nType number of piece to move: "))
# search the puzzle for the piece numbered num:
piece = () # remembers the cell coordinates where num rests
for i in range(PUZZLE_SIZE):
for j in range(PUZZLE_SIZE) :
if num == puzzle[i][j] :
piece = (i, j) # we found it at cell i,j
break
# The previous loops are finished; did we find the piece ?
if piece == () :
print "illegal move --- try again"
else : # we did find it, so let's try to move it:
# First, check if the piece is adjacent to the empty space:
i = piece[0]
j = piece[1]
if (empty_space == (i-1, j)) \
or (empty_space == (i+1, j)) \
or (empty_space == (i, j-1)) \
or (empty_space == (i, j+1)) :
# True, so it's ok to move num into the empty space:
puzzle[empty_space[0]][empty_space[1]] = num
# remember that the former location of num is now empty:
puzzle[i][j] = 0
empty_space = (i,j)
else : # False, so it's not ok to move num:
print "illegal move --- try again"
===============================================
FIGURE 7=====================================================
using System;
/// <remarks> SlidePuzzle implements a slide-puzzle game.
/// assumed input: a series of integers between 1 and 15, which indicate the
/// pieces to move in the slide puzzle.
/// guaranteed output: the slide puzzle, as it appears after each move
/// <remarks>
public class SlidePuzzle
{ public static void Main()
{
int PUZZLE_SIZE = 4; // how large we make the puzzle
// the puzzle, where 0 marks the empty space/cell:
int[,] puzzle = { {15, 14, 13, 12}, // use braces, not brackets,
{11, 10, 9, 8}, // to initialize an array
{ 7, 6, 5, 4},
{ 3, 2, 1, 0} };
int empty_x = 3;
int empty_y = 3; // the x,y-coordinates of the empty space
while (true) {
// print the puzzle:
int count = 0;
foreach ( int n in puzzle )
{ Console.Write(n);
Console.Write(" ");
count = count + 1;
if ( count % PUZZLE_SIZE == 0 )
{ Console.WriteLine(); }
}
// ask for the next move:
Console.Write("Type number of piece to move : ");
int num = Int32.Parse(Console.ReadLine());
// search the puzzle for the piece numbered num:
int found_x = -1;
int found_y = -1;
for ( int i = 0; i != PUZZLE_SIZE; i = i + 1 )
{ for ( int j = 0; j != PUZZLE_SIZE; j = j + 1 )
{ if ( num == puzzle[i,j] )
{ found_x = i;
found_y = j;
break; // we found num at cell i,j
}
}
}
// The previous loops are finished; did we find the piece ?
if ( found_x == -1 )
{ Console.WriteLine("illegal move --- try again"); }
else // we did find it, so let's try to move it:
{ // First, check if the piece is adjacent to the empty space
if ( ((empty_x == found_x-1) && (empty_y == found_y))
|| ((empty_x == found_x+1) && (empty_y == found_y))
|| ((empty_x == found_x) && (empty_y == found_y-1))
|| ((empty_x == found_x) && (empty_y == found_y+1)) )
// If true, it's ok to move num into the empty space:
{ puzzle[empty_x,empty_y] = num;
puzzle[found_x,found_y] = 0;
empty_x = found_x;
empty_y = found_y;
}
else { Console.WriteLine("illegal move --- try again"); }
}
} // end while-loop
}
}
ENDFIGURE=========================================================
Because C# does not have tuples, we cannot save the coordinates of the puzzle's empty space as a pair. Note also that the elements of a two-dimensional array can be processed by a single foreach-loop: the elements are enumerated in row-major order (all of row 0, then all of row 1, etc.).
We can also model the puzzle as a nested array, but we cannot use
the convenient set-braces notation to initialize the nested array ---
we must write for-loops:
int[][] puzzle = new int[4][];
for ( int row = 0; row < 4; row = row + 1 )
{ puzzle[row] = new int[4];
for ( int col = 0; col < 4; col = col + 1 )
{ puzzle[row][col] = 16 - ((row * 4) + col + 1); }
}
When we print the nested array, we use nested loops:
foreach (int[] row in puzzle )
{ foreach (int num in row )
{ Console.Write(num); Console.Write(" "); }
Console.WriteLine();
}
An individual element is indexed just like in Python, with two
bracketed indexes, e.g.,
puzzle[found_x][found_y] = 0;
What about dictionaries? Can we easily simulate them in C#? Well, no. In the next course, CIS300, you will learn how to build a hash table implementation of a dictionary in C#. (Or, you can use the version prewritten for you in the .NET library,)
Functions in C# operate like the ones in Python.
Here are three simple examples from Lecture 6.1, reformatted into C#,
along with a Main function that calls them:
Figure 8=========================================
using System;
/// <remarks> ExampleFunctions defines and tests three functions. </remarks>
public class ExampleFunctions
{
/// <summary> error prints an annoying error message </summary>
public static void error()
{ string text = "ErrorErrorErrorErrorErrorErrorError\n";
Console.WriteLine(text + text + text);
}
/// <summary> printBigError prints an error message surrounded
/// by an annoying border. </summary>
/// <param name="message"> the error message </param>
public static void printBigError(string message)
{ error();
Console.WriteLine(message);
error();
}
/// <summary> reciprocal computes the reciprocal of its argument </summary>
/// <param name="n"> an int, which must not equal 0 </param>
/// <returns> 1.0/n (When n==0, 0.0 is returned.) </returns>
public static double reciprocal(int n)
{ double answer = 0.0;
if ( n != 0 )
{ answer = (1.0 / n); }
return answer;
}
/// <summary>Main tests the functions. </summary>
public static void Main()
{ double d = reciprocal(5);
printBigError("one fifth is " + d + " ?!");
}
}
=================================================
Note that
public static double reciprocal(int n)defines the reciprocal function, which requires an int argument and returns an answer that is a double. (See below for an explanation of static.)
C# functions are best used to build modules, so we move directly to this subject.
Here is the clock module, slightly changed, from Lecture 6.2:
=========================================================
# Clock models a clock that ticks and emits an alert every 60 ticks
time = 0 # holds the time
def tick() :
"""tick increments the time by one tick and emits an alert every 60 ticks"""
global time
time = time + 1
if (time % 60) == 0 :
print "Clock alert: time is", time
def getTime() :
"""getTime returns the current time"""
global time
return time
def reset(new_time) :
"""reset resets the clock to the new_time"""
global time
time = new_time
=================================================
Now, here is its recoding in C#:
FIGURE 9======================================================
using System;
/// <remarks> Clock models a clock that ticks and emits an alert
/// every 60 ticks. </remarks>
public class Clock
{ private int time; // the time
/// <summary> This is the ''constructor'' (__init__) method,
/// which sets the starting values of the variables. </summary>
/// <param name="init_time"> the initial time for the clock. </param>
public Clock(int init_time)
{ time = init_time; }
/// <summary> tick increments the clock by one tick </summary>
public void tick()
{ time = time + 1;
if ((time % 60) == 0 )
{ Console.WriteLine("Alert: " + time); }
}
/// <summary> getTime returns the current time. </summary>
/// <returns> the current time </returns>
public int getTime()
{ return time; }
/// <summary> reset resets the clock to a new time </summary>
/// <param name="new_time"> the time to reset </param>
public void reset(int new_time)
{ time = new_time; }
}
ENDFIGURE=====================================================
The variable, time, and its maintenance functions are
listed within the brackets of class Clock.
The coding looks as expected, but notice the keyword,
private, in the definition,
private int time; // the time
The word private asserts that variable time cannot
be referenced from outside the class. Indeed, only tick
and getTime can be referenced, because they are labelled
by the keyword, public.
Also, we have added an ``init'' function to the class, which must have the same name as the class itself. The function is called a constructor method, and it is used to initialize the private variables. There is no need for a Main function.
Finally, notice the removal of the static keywords from the header lines of the functions. (Again, use static to label functions that are called from Main.)
The Clock module will be used as a helper module to another program.
So, we save it in the file, Clock.cs, and
we compile it, like this:
csc /debug+ /t:library Clock.cs
The extra notation, /t:library, says that the ``target code''
produced by the C# compiler will be a library file (a .dll-file)
that can be
connected to another program. When the compiler finishes, it produces
the file, Clock.dll.
To use class Clock, study this
test program:
FIGURE 10==================================================
using System;
using System.Threading; // a module used for the ``sleep'' trick below
/// <remarks> TestClock shows a ticking clock. </remarks>
public class TestClock
{ public static void Main()
{ int START = 50;
Clock c = new Clock(START); // construct the clock, named c
while ( true )
{ // tell the clock to tick and display the time:
c.tick();
Console.WriteLine(c.getTime());
// Here is a trick that
// makes the program stop (sleep) for 1000 milliseconds:
Thread.Sleep(1000);
}
// alas, we _cannot_ do this:
// Console.WriteLine(c.time);
// this is illegal because variable time in Clock is _private_
}
}
==========================================================
The program shows that we activate a ``module'' with this command:
Clock c = new Clock();
which names the module, c.
We use the module's components in the usual way:
c.tick();
Console.WriteLine(c.getTime());
but we cannot use the module's private variable; this is illegal:
Console.WriteLine(c.time);
C# makes good use of the private keyword
to prevent unacceptable updates of the variables in a module ---
only the module's public functions are allowed to update
the private variables.
As usual, we save the program in a file, TestClock.cs.
When we compile the program, we tell the C# compiler to link
the clock module to the program:
csc /debug+ /R:Clock.dll TestClock.cs
The result will be an executable program, TestClock.exe.
There is one other way to construct a module in C# --- this is the ``static'' version:
private static int time = 0;
This makes Clock into a file-of-functions, like one would write in the language, C.
The static module looks like this:
FIGURE 11===============================
using System;
public class Clock1
{ private static int time; // the time
public static void tick()
{ time = time + 1;
if ((time % 60) == 0 )
{ Console.WriteLine("Alert: " + time); }
}
public static int getTime()
{ return time; }
public static void reset(int new_time)
{ time = new_time; }
}
====================================
Now, the functions can be called directly by
a Main function.
FIGURE 12================================= using System; using System.Threading; public class TestClock1 { public static void Main() { while ( true ) { Clock1.tick(); Console.WriteLine(Clock1.getTime()); Thread.Sleep(1000); } } } ===================================
Given class Clock seen earlier, we might write the following
test program, which builds 3 clock objects and synchronizes them to
count seconds, minutes, and hours:
FIGURE 13=====================================================
/// <remarks> TestClock2 shows how to construct 3 clocks that are
/// synchronized to count seconds, minutes, and hours. </remarks>
public class TestClock2
{ public static void Main()
{ Clock hours = new Clock(0);
Clock minutes = new Clock(0);
Clock seconds = new Clock(0);
while ( true )
{ // tell the seconds clock to tick:
seconds.tick();
if ( seconds.getTime() == 60 ) // one full minute?
{ minutes.tick();
seconds.reset(0);
}
if ( minutes.getTime() == 60 ) // one full hour?
{ hours.tick();
minutes.reset(0);
}
Console.WriteLine(hours.getTime() + ":" + minutes.getTime()
+ ":" + seconds.getTime());
// make the program sleep 100 milliseconds:
Thread.Sleep(100);
}
}
}
ENDFIGURE=========================================================
We constructed 3 separate clocks (and 3 namespaces in
computer storage) from
Clock hours = new Clock(0);
Clock minutes = new Clock(0);
Clock seconds = new Clock(0);
====================================================== class Smiley(object) : """Smiley objects know their name and age.""" def __init__(self, new_name) : """__init__ constructs a new Smiley object. It holds two variables: name, a string age, an integer (initialized to 0). parameters: self - the new object we are constructing new_name - the string that we assign to variable, name """ self.name = new_name # make the object hold a name self.age = 0 # make the object hold an age print "Smiley object named", self.name, "constructed." # print trace info def talk(self) : """talk makes an object say its name and age. parameter: self - the object that will talk """ print "(-: I am", self.name, "and I am", self.age, "days old." self.age = self.age + 1 def tellSecret(self) : """tellSecret makes an object tell a ``secret message'' parameters: self - the object that will tell the secret returns: a string, the secret """ return "I don't know" ==========================================================
Here it is, reformatted in C#:
FIGURE 14================================================
using System;
/// <remarks> Smiley objects know their name and age </remarks>
public class Smiley
{
private String name; // holds the object's name
private int age; // holds the object's age
/// <summary> Constructor Smiley constructs a new Smiley. </summary>
/// <param name="new_name">the object's name </param>
public Smiley(String new_name)
{ name = new_name;
age = 0;
Console.WriteLine("Smiley object named " + name + " constructed.");
}
/// <summary> talk makes an object say its name and age </summary>
public void talk()
{ Console.WriteLine("(-: I am " + name + " and I am " + age
+ " days old.");
age = age + 1;
}
/// <summary> tellsecret makes an object tell a ``secret message'' </summary>
/// <returns> a string, the secret </returns>
public virtual String tellSecret()
{ return "I don't know"; }
}
ENDFIGURE=====================================================
The changes in the C# version are
name = new_name; age = 0;
Here is how we use a C# class to construct three Smiley objects:
FIGURE 15===============================================
public class TestSmiley
{ public static void Main()
{ Smiley s1 = new Smiley("larry");
s1.talk();
s1.talk();
Smiley s2 = new Smiley("moe");
s2.talk();
s1.talk();
Smiley s3 = new Smiley("curly");
s1.talk();
s2.talk();
s3.talk();
}
}
==============================================
Note the use of the keyword, new, each time a new object
is constructed.
Finally, here is the SmartSmiley class from Python, which
showed how one class can build upon (extend) another:
===============================================
from TextSmiley import Smiley
class SmartSmiley(Smiley) : # this class extends class Smiley
"""SmartSmiley is a Smiley that can also remember and tell secrets."""
def __init__(self, new_name, my_secret) :
"""__init__ constructs a new SmartSmiley object
It holds the variables from a Smiley -- name and age -- along with
a new variable, secret.
parameters: self - the new object we are constructing
new_name - a string that we assign to name
my_secret - a string that we assign to secret
"""
Smiley.__init__(self, new_name) # call Smiley's __init__ function
self.secret = my_secret # define a new variable, secret
def tellSecret(self) : # this cancels the version of tellSecret in Smiley
"""tellSecret makes an object tell a ``secret message''.
parameters: self - the object that will tell the secret
returns: a string, the secret
"""
return self.secret
def learnsSecretOf(self, anotherSmiley) :
"""learnsSecretOf lets one object learn another object's secret message
parameters: self - the object that will learn a new secret
anotherSmiley - a Smiley object that will surrender its secret
"""
self.secret = self.secret + ", and " + anotherSmiley.tellSecret()
def test():
s1 = SmartSmiley("larry", "eat your vegetables")
s1.talk()
print s1.tellSecret()
s2 = SmartSmiley("moe", "get enough sleep")
s1.learnsSecretOf(s2)
print s1.tellSecret()
s3 = Smiley("curly")
print s3.tellSecret()
s1.learnsSecretOf(s3)
print s1.tellSecret()
s3.learnsSecretOf(s1)
Smiley.__init__(s3,"ed")
print Smiley.tellSecret(s3)
test()
===========================================================
In C#, we have
FIGURE 16===================================================
/// <remarks>SmartSmiley objects are Smileys that remember secrets. </remarks>
public class SmartSmiley : Smiley // the base class is Smiley
{
private String secret; // the secret remembered by a SmartSmiley
/// <summary>Constructor SmartSmiley makes a new SmartSmiley. </summary>
/// <param name="new_name"> the object's name </param>
/// <param name="my_secret"> this object's secret </param>
public SmartSmiley(String new_name, String my_secret) : base(new_name)
// The previous phrase calls the constructor (init) method for Smiley.
{ secret = my_secret;}
/// <summary> an improved coding of tellSecret for SmartSmileys </summary>
public override String tellSecret()
{ return secret; }
/// <summary> learnsSecretOf asks another Smiley for its secret and
/// remembers it. </summary>
/// <param name="anotherSmiley">the Smiley asked to tell its secret </param>
public void learnsSecretOf(Smiley anotherSmiley)
{ secret = secret + ", and " + anotherSmiley.tellSecret(); }
}
ENDFIGURE=======================================================
Notice
public class SmartSmiley : Smileystates that SmartSmiley extends (includes) the coding of base class Smiley.
public SmartSmiley(String new_name, String my_secret) : base(new_name)contains the extra phrase, base(new_name). This calls the constructor (init) method of class Smiley, which is essential for initializing the variables in the base-class part of the new SmartSmiley.
public override String tellSecret()contains the keyword, overrides, which signals that this new coding of tellSecret cancels the earlier coding for SmartSmiley objects. (Smiley objects will use the coding of tellSecret that is listed within class Smiley.)
Some test code:
FIGURE 17==========================================================
using System;
public class TestSmart
{ public static void Main()
{ SmartSmiley s1 = new SmartSmiley("larry", "eat your vegetables");
s1.talk();
Console.WriteLine(s1.tellSecret());
SmartSmiley s2 = new SmartSmiley("moe", "get enough sleep");
s1.learnsSecretOf(s2);
Console.WriteLine("larry says: " + s1.tellSecret());
Smiley s3 = new Smiley("curly");
Console.WriteLine("curley says: " + s3.tellSecret());
s1.learnsSecretOf(s3);
Console.WriteLine("moe says: " + s1.tellSecret());
// We cannot do this:
// s3.learnsSecretOf(s1);
}
}
=============================================================