for RuBoard |
Our "Hello, World" program illustrated the basic structure of a C# program, but we will need a slightly more elaborate example to show the use of other basic programming constructs, such as variables , expressions, and control structures. Our next example is a simple calculator for an IRA account. We calculate the accumulation of deposits to an IRA of $2000.00 a year at 6% interest for 10 years , assuming that each deposit is made at the end of the year. Our calculation is performed two ways:
In a loop, year by year, accumulating a total as we go
Using a formula
The example program is in the folder Ira\Step1.
// Ira.cs - Step 1 using System; class Ira { public static int Main(string[] args) { int years = 10; decimal rate = 0.06m; decimal amount = 2000M; decimal interest; decimal total = 0m; Console.WriteLine("{0,4} {1,12} {2,12} {3,12}", "Year", "Amount", "Interest", "Total"); for (int i = 1; i <= years; i++) { interest = total * rate; total += amount + interest; Console.WriteLine( "{0, -4} {1, 12:C} {2, 12:C} {3, 12:C}", i, amount, interest, total); } Console.WriteLine("\nTotal using formula = {0}", IraTotal(years, (double) rate, (double) amount)); return 0; } public static double IraTotal(int years, double rate, double amount) { double total = amount * (Math.Pow(1 + rate, years) - 1) / rate; long total_in_cents = (long) Math.Round(total * 100); total = total_in_cents /100.0; return total; } }
If you compile and run it, you will see this output:
Year Amount Interest Total 1 ,000.00Year Amount Interest Total 1 $2,000.00 $0.00 $2,000.00 2 $2,000.00 $120.00 $4,120.00 3 $2,000.00 $247.20 $6,367.20 4 $2,000.00 $382.03 $8,749.23 5 $2,000.00 $524.95 $11,274.19 6 $2,000.00 $676.45 $13,950.64 7 $2,000.00 $837.04 $16,787.68 8 $2,000.00 $1,007.26 $19,794.94 9 $2,000.00 $1,187.70 $22,982.63 10 $2,000.00 $1,378.96 $26,361.59 Total using formula = 26361.59.00 ,000.00 2 ,000.00 0.00 ,120.00 3 ,000.00 7.20 ,367.20 4 ,000.00 2.03 ,749.23 5 ,000.00 4.95 ,274.19 6 ,000.00 6.45 ,950.64 7 ,000.00 7.04 ,787.68 8 ,000.00 ,007.26 ,794.94 9 ,000.00 ,187.70 ,982.63 10 ,000.00 ,378.96 ,361.59 Total using formula = 26361.59
In C# variables are of a specific data type. Some common types are int for integers and double for floating-point numbers . C# has the decimal data type, which has a high degree of precision, suitable for monetary calculations.
You must declare and initialize variables before you can use them.
int years = 10; // reserves space and assigns // an initial value decimal interest; // reserves space but does // not initialize it to any value
If an initial value is not specified in the declaration, the variable must be initialized in code before it can be used. We will discuss initialization later in the chapter.
Variables must be either local within a method or members of a class. There are no global variables in C#.
A literal is used when you explicitly write a value for a variable in a program. An integer literal is represented by either an ordinary decimal integer or a hexadecimal integer. A floating-point or decimal literal is represented by a number with a decimal point or by exponential notation. You may influence the type [1] that is used for storing a literal by a suffix. The suffix f or F indicates single precision floating point. The suffix d or D indicates double precision floating point. The suffix m or M indicates decimal (think money).
[1] We discuss C# types, such as float , double , and decimal , later in the chapter.
decimal rate = 0.06m; decimal amount = 2000M;
There are two forms for string literals. Escape sequences are not processed for string literals that are prefixed with @ .
string file1 ="c:\test1.txt"; string file2 = @"c:\test2.txt";
You can combine variables and literals via operators to form expressions. The C# operators are similar to those in C and C++, having similar precedence and associativity rules. There are three kinds of operators,
Unary operators take one operand and use prefix notation (e.g., --a ) or postfix notation (e.g., a++ ).
Binary operators take two operands and use infix notation (e.g., a + b ).
The one ternary operator ?: takes three operands and uses infix notation (e.g., expr ? x : y ).
Operators are applied in the precedence order shown in Table 3-1. For operators of the same precedence, order is determined by associativity.
The assignment operator is right-associative (operations are performed from right to left).
All other binary operators are left-associative (operations are performed from left to right).
Precedence and associativity can be controlled by parentheses; what is done first is shown as the primary operator (x) in the precedence table. C# has checked and unchecked operators, which will be discussed later.
Category | Operators |
---|---|
Primary | (x) x.y f(x) a[x] x++ x- new typeof sizeof checked unchecked |
Unary | + - ! ~ ++x --x (T)x |
Multiplicative | * / % |
Additive | + - |
Shift | << >> |
Relational | < > <= >= is as |
Equality | == != |
Logical AND | & |
Logical XOR | ^ |
Logical OR |
|
Conditional AND | && |
Conditional OR |
|
Conditional | ?: |
Assignment | = *= /= %= += -= <<= >>= &= ^= = |
The Console class in the System namespace supports two simple methods for performing output:
WriteLine writes out a string followed by a new line.
Write writes out just the string without the new line.
You can write out other data types by relying on the ToString method of System.Object , which will provide a string representation of any data type. We will discuss the root class System.Object in Chapter 5, where you will also see how to override ToString for your own custom data type. You can use the string concatenation operator + to build up an output string.
int x = 24; int y = 5; int z = x * y; Console.Write( " Product of " + x + " and " + y); Console.WriteLine( " is " + z);
The output is all on one line:
Product of 24 and 5 is 120
A more convenient way to build up an output string is to use placeholders {0}, {1}, and so on. An equivalent way to do the output shown above is
Console.WriteLine("Product of {0} and {1} is {2}", x,y,z);
The program OutputDemo illustrates the output operations just discussed.
We will generally use placeholders for our output from now on. Placeholders can be combined with formatting characters to control output format.
C# has extensive formatting capabilities, which you can control through placeholders and format strings.
Simple placeholders: {n}, where n is 0, 1, 2, . . . , indicating which variable to insert
Control width: {n,w}, where w is width (positive for right-justified and negative for left-justified) of the inserted variable
Format string: {n:S}, where S is a format string indicating how to display the variable
Width and format string: {n,w:S}
A format string consists of a format character followed by an optional precision specifier . Table 3-2 shows the available format characters.
Format Character | Meaning |
---|---|
C | Currency (locale specific) |
D | Decimal integer |
E | Exponential (scientific) |
F | Fixed point |
G | General (E or F) |
N | Number with embedded commas |
X | Hexadecimal |
The program FormatDemo illustrates formatting. Our sample program Ira\Step1 provides another example. The header uses width specifiers, and the output inside the loop uses width specifiers and the currency format character.
... Console.WriteLine("{0,4} {1,12} {2,12} {3,12}", "Year", "Amount", "Interest", "Total"); for (int i = 1; i <= years; i++) { interest = total * rate; total += amount + interest; Console.WriteLine( "{0, -4} {1, 12:C} {2, 12:C} {3, 12:C}", i, amount, interest, total); } ...
The preceding code fragment illustrates a for loop. The C# control structures include the familiar control structures of the C family of languages,
if
while
do
for
switch
break
continue
return
goto
These all have standard semantics, except for switch , which is less error-prone in C#. There are several other control statements in C#:
There is a foreach loop, which we will discuss later in connection with arrays and collections.
The throw statement is used with exceptions. We will discuss exceptions later in this chapter.
The lock statement can be used to enforce synchronization in multithreading situations. We will discuss multithreading in Chapter 8.
In C#, after a particular case statement is executed, control does not automatically continue to the next statement. You must explicitly specify the next statement, typically by a break or goto label . (As in C and C++, you may call for identical handling of several cases by having empty statements for all the case labels except the last one.) In C# you may also switch on a string data type. The program SwitchDemo illustrates use of the switch statement in C#.
... switch(scores[i]) { case 1: Console.Write("Very "); goto case 2; // cannot fall through case 2: Console.WriteLine("Low"); break; case 3: Console.WriteLine("Medium"); break; case 4: case 5: Console.WriteLine("High"); break; default: Console.WriteLine("Special Case"); break; } ...
Our Ira\Step1 example program has a method IraTotal for computing the total IRA accumulation by use of a formula. In C# every function is a method of some class; there are no freestanding functions. If the method does not refer to any instance variables of the class, the method can be static . We will discuss instance data of a class later in this chapter. Since the method is accessed only from within the class, it is designated as private .
Note the use of the Pow and Round methods of the Math class, which is another class in the System namespace. These methods are static methods. To call a static method from outside the class in which it is defined, place the name of the class followed by a period before the method name . In C# you cannot employ the alternative C++ style of using an instance name to qualify a static method.
... private static double IraTotal(int years, double rate, double amount) { double total = amount * (Math.Pow(1 + rate, years) - 1) / rate; long total_in_cents = (long) Math.Round(total * 100); total = total_in_cents /100.0; return total; } ...
Our first Ira program is not too useful, because the data are hardcoded. To perform the calculation for different data, you would have to edit the source file and recompile. What we really want to do is allow the user of the program to enter the data at runtime.
An easy, uniform way to do input for various data types is to read the data as a string and then convert to the desired data type. Use the ReadLine method of the System.Console class to read in a string. Use the ToXxxx methods of the System.Convert class to convert the data to the type you need.
Console.Write("amount: "); string data = Console.ReadLine(); amount = Convert.ToDecimal(data);
Although console input in C# is fairly simple, we can make it even easier using object-oriented programming. We can encapsulate the details of input in an easy-to-use wrapper class, InputWrapper (which is not part of the .NET Framework class library).
In C# you instantiate a class by using the new keyword.
InputWrapper iw = new InputWrapper();
This code creates the object instance iw of the InputWrapper class.
The InputWrapper class wraps interactive input for several basic data types. The supported data types are int , double , decimal , and string . Methods getInt , getDouble , getDecimal , and getString are provided to read those types from the command line. A prompt string is passed as an input parameter. The directory InputWrapper contains the files InputWrapper.cs , which implements the class, and TestInputWrapper.cs , which tests the class. (For convenience, we provide the file InputWrapper.cs in each project where we use it.)
You can use the InputWrapper class without knowing its implementation. With such encapsulation, complex functionality can be hidden by an easy-to-use interface. (A listing of the InputWrapper class is in the next section.)
Here is the code for Ira\Step2 . We read in the deposit amount, the interest rate, and the number of years, and we compute the IRA accumulation year by year. The first input is done directly, and then we use the InputWrapper class. The bolded code illustrates how to use the InputWrapper class. Instantiate an InputWrapper object iw by using new . Prompt for and obtain input data by calling the appropriate getXXX method.
// Ira.cs - Step 2 using System; class Ira { public static int Main(string[] args) { InputWrapper iw = new InputWrapper(); decimal amount; // annual deposit amount decimal rate; // interest rate int years; // number of years decimal total; // total accumulation decimal interest; // interest in a year Console.Write("amount: "); string data = Console.ReadLine(); amount = Convert.ToDecimal(data); rate = iw.getDecimal("rate: "); years = iw.getInt("years: "); total = 0m; Console.WriteLine("{0,4} {1,12} {2,12} {3,12}", "Year", "Amount", "Interest", "Total"); for (int i = 1; i <= years; i++) { interest = total * rate; total += amount + interest; Console.WriteLine( "{0, -4} {1, 12:C} {2, 12:C} {3, 12:C}", i, amount, interest, total); } Console.WriteLine("\nTotal using formula = {0}", IraTotal(years, (double) rate, (double) amount)); return 0; } private static double IraTotal(int years, double rate, double amount) { double total = amount * (Math.Pow(1 + rate, years) - 1) / rate; long total_in_cents = (long) Math.Round(total * 100); total = total_in_cents /100.0; return total; } }
The program in Ira\Step2 is our first example of the common situation of a program with multiple files (in this case, just two: Ira.cs and InputWrapper.cs ). It is easy to compile multiple files at the command line.
> csc /out:Ira.exe *.cs
This will compile all the files in the current directory. You should use the /out option to specify the name of the output file.
If multiple classes contain a Main method, you can use the /main command-line option to specify which class contains the Main method that you want to use as the entry point into the program.
>csc /main:Ira /out:Ira.exe *.cs
The InputWrapper class is implemented in the file InputWrapper.cs . You should find the code reasonably intuitive, given what you already know about classes.
// InputWrapper.cs // // Class to wrap simple stream input // Datatype supported: // int // double // decimal // string using System; class InputWrapper { public int getInt(string prompt) { Console.Write(prompt); string buf = Console.ReadLine(); return Convert.ToInt32(buf); } public double getDouble(string prompt) { Console.Write(prompt); string buf = Console.ReadLine(); return Convert.ToDouble(buf); } public decimal getDecimal(string prompt) { Console.Write(prompt); string buf = Console.ReadLine(); return Convert.ToDecimal(buf); } public string getString(string prompt) { Console.Write(prompt); string buf = Console.ReadLine(); return buf; } }
Note that, unlike the method IraTotal , the methods of the InputWrapper class are used outside of the class so they are marked as public .
If bad input data is presented, an exception will be thrown. Exceptions are discussed later in this chapter.
for RuBoard |