C#
C#
C#
Get the gist of the concept Write the program See the output for yourself
Quasar S. Chunawalla
1|Page
2008
These notes were compiled by me, while serving NIIT Training Institute, Mumbai India as a faculty member. This text would facilitate the learning process of the students when they learn an Object-Oriented Programming language like C#. It starts from scratch and takes you upto the depth required at an undergraduate school. I appreciate the contribution made by my students Farzan, Mario, Swapnil, Geetashree, Swati, Pooja, Shraddha, Chaitali, Snehal, Sushil, Anand, Sadiq, Rama, Tausif, Ameya and all other students and colleagues.
2|Page
2008
Contents
Chapter 1 2 3 4 5 6 7 8 9 10 11 12
Name Introduction to C# C# Fundamentals Decision Making and Looping Constructs Classes, Objects and Methods Collections in C# Polymorphism, Constructors and Destructors Inheritance Our objects family tree Advanced C# Language Concepts File Input and Output Threads, Processes, AppDomains and Multi-threading Exception Handling Assembly and Reflections
Pages 1 11
3|Page
2008
4|Page
1
Introduction to C#
Objectives
To type a simple program in C#, and acquaint yourself with editing, compiling and running your program. d How the HelloWorld program works... Hey, Object Oriented Programming the other day I heard these words from geek next door. Familiarize yourself with the concept of Objects and Classes. Have a dim and faint idea of Object Oriented features like Abstraction, Encapsulation, Polymorphism and Inheritance
This chapter serves as a preliminary introduction to students who are new to C# paradigm. In C#, you can create two kinds of applications f Console Based Applications and Windows Applications Throughout Applications. the first half of this book, you will learn how to create a Console Application. Console Applications accept input and display output in . the form text. In console programs, generally, you will use the generally, Console window to give inputs and see the output. Windows Applications usually have a user interface(GUI) through which you user-interface(GUI) give inputs and see outputs.
1|Page
2008
In MS Windows 95/98, the console is called MS-DOS Prompt. In Windows XP, Vista, it is called the Command Prompt.
You begin by punching in the above program in any text editor like notepad on Microsoft Windows. You save the file in a convenient location, say C:\ drive. Lets save the file as Greetings.cs.
Once you are done with that, well go to Start => All Programs => Microsoft Visual Studio 2008 => Visual Studio Tools => Visual Studio 2008 Command Prompt. In the Command Prompt, navigate to the location, where you stored your Greetings.cs file. Once you reach there, To compile the program, type csc Greetings.cs To run the program, type Greetings
2|Page
2008
Line 1 of the program reads using System. This is called a using Directive. It says that this program borrows some features from the System namespace. In C#, a namespace is a way to group related features together. A namespace contains some ready-made code, that is available at your disposal. You can refer to it and use it in your program, rather than re-writing it. You dont have to re-invent the wheel everytime. Hold that for a moment now, I will explain it to you at length further ahead. Line 2 of the program is a blank line. Programmers often use spaces and blank lines to separate different sections of code, just as we leave space between two paragraphs in the English language. 1.1 Good Programming Practice
You must use whitespaces, blank lines to make the program readable, so that others can easily comprehend it.
3 class Greeting
Lines 3-9 of the program consists of our first class. The dictionary meaning of the English word class is the category in which things fall. A number of things often share some common features and operations. For example, all color TV objects have features like volume, brightness, contrast, color and have operations such as increase/decrease volume, increase/decrease brightness, increase/decrease contrast etc. All such objects would fall in the
3|Page
2008
colorTV class. In this program, weve taken a class Greeting. To specify that Greeting is a class, we write the class keyword before it. Keywords are word that have special meaning in the C# system. Thus, class keyword tells the computer that Greeting is a class. Line 4 is an opening curly brace {. The curly brace indicates the st . start of the class. When the class ends, we have to close the class by putting the corresponding } closing curly brace. The matter enclosed in the curly braces is called the class body. In C#, when you write a . new class, you will always pack the body of the class inside a pair of curly braces{...}. 1.1 Common Programming Error
The number of opening curly braces must equal the number of closing curly braces. Many Many-atimes, naive programmers start a curly brace, but forget to close it.
Line 5 is present in all C# programs. The word Main() indicates this programs. is the start of the program. This is where the C# system start starts executing(running) the program. Thus, Main() is the entry point(starting point) of any program. All statements written inside Main() are executed one by one, one line and the next line and so on. The Main() block is started using a starting curly brace { and terminated using an ending curly brace }. Whatever you want to write . inside Main() must be enclosed within this pair of curly braces. Main() is called a method/operation. Every method has a method/operation. parenthesis (). Main is the name of the method. The contents written inside Main() block is called method body.
4|Page
2008
In the above program, we have written just one command/statement Console.WriteLine().This command is used display text on the VDU This screen. Whatever you put inside Console.WriteLine() in the double quotes gets printed on the screen, when you run the program. In this case, it is Hello World. At the end of the statement, there is a sem semi-colon. Just as every English sentence ends with a full stop, every statement in C# ends full-stop, with a semi-colon;. 1.2 Common Programming Error
Forgetting to put the delimiter ; after every statement is a committed quite often. As a rule, every C# statement must end with a semi colon. If you miss out any, your program will not semi-colon. compile successfully. The C Sharp Compiler csc will prompt you about the point, where it expects a semi-colon.
5|Page
2008
2008
name, brand, year of manufacture, price, color which describe it. In C#, to model this concept, C# car object will also have attributes name, brand, yearOfManufacture, price, color etc. For example, my car might have the name Maruti, brand as Swift, yearOfManufacture as 2008, price as 3.5 Lac, color as Blue. An object primarily contains two things data in the form of attributes and methods things that operate on this data. We now know what attributes mean. What are methods? Well, in Object-Oriented Programming, we often like to change the values of the attributes. For example, 3 years down the lane, you would like to refurbish your car with a fresh new paint, make it waterrepellant using a good old teflon coating. Also, as years pass by the price of your car changes due to depreciation. The true price of your car is the depreciated value. Thus, we often need to change the attributes(data) inside the object. There are two ways to do this. First is, we go in and modify each value directly. However, this is not a good practice in Object Oriented Programming. It is not a good practice to modify/access the attributes of an object directly. Instead, we access the attributes/data of an object through the methods in an object. Anything we would like to do, on an objects data, we must do it through the methods. Methods act as an interface between the object (its data) and the outside world user.
To model real-world things or entities, in C# we create Objects. Capsule of Data(Attributes) and Methods(Operations on Data) is an Object. Attributes are the characteristics of features that describe the object. Methods are used to manipulate the data/attributes of an Object. Not a good practice to access the attributes of an object directly. Do it through methods. Methods act as a broker, they have the license to access the attributes.
7|Page
2008
On the same lines, in C#, we first create a design or a plan called Class. Using this class/plan as a blue print or base, we construct s blue-print actual objects. The class/plan tells us how the objects are going to look like.. A class must specify what attributes and methods will be there in an object. For example, before creating the different car objects, we must car specify in the plan(class), what attributes and methods every car object would have. We then construct Car objects, by assigning values to each of the Cars attributes.
8|Page
2008
Thus, in C#, we must create a class(plan) and then use it a guideline(blue-print) to create objects. You cannot create objects without a class. Also, merely defining a class is not enough. To be able to use it, we must create objects of the class. Just as having a drawing on paper is just a plan. To materialise this plan, we must utilize it to construct a building.
1.6 Abstraction
Abstraction is the process of stripping down an onion, peeling off all the inessential layers, until you are left with the bare minimum essentialities, the kernel or core. Abstraction steers clear of all unimportant details and allows you to pay more emphasis on the important details. Abstraction is an important software design principle. A good class always exposes a minimal set of functionalities that allow you to manipulate the objects in an easy-to-use fashion. Let me cite to you an example of abstraction which you might already know. To transfer a file from your desktop PC to a USB flash drive, you would have to specify, where from to lift the file on the hard-disk, the track, the sector, the cluster, how many bytes to transfer, the destination location etc. With the help of an Operating System like Microsoft Windows, we just have to copy the file and paste it where we would like to. What an Operating System does is, it frees us from all the unessential details of hardware. Thus, it provides a higher level abstraction, and hides all the unnecessary details. Why abstraction is so important? Computer applications or software are generally made of components or layers. As time progresses, new functionalities and features are added to them. When new layers are added, we would want that their impact on the older layers should be minimal. Minimal dependency is an absolute must. This is why abstraction is necessary.
9|Page
2008
1.8 Inheritance
In a family, a child inherits the features and traits of his parent, and parents inherit their features and characteristics from grand-parents. This way, certain features or attributes are passed on from generationto-generation. A similar tool is present in C# as well. An object can inherit attributes and methods from another object. The former is called the parent, and the latter is called the child. The child object has features and attributes of the parent. In addition to this, the child object can have some of its own features and attributes. Thus, a child is a specialised kind of a parent. They share a kind-of relationship.
1.9 Polymorphism
Poly means many, morphos means form. Polymorphism means one thing exhibiting many forms. In polymorphism, an object behaves differently in different situations. Depending upon the context and environment, it changes its behaviour.
10 | P a g e
2008
2
C# Fundamentals
Objectives
Learn how to store data in computer memory use variables and constants Rules to be followed while you assign a valid variable name Declaring Variables in C# Data-types in C# - They come in various sizes, and store different types of types data Assigning values to variables Learn with a simple program to store and retrieve values of variables Operators in C# Get your hands wet with learning how to use arithmetic operators wherever you want. Study a simple program that calculates the Simple Intere Interest
2008
Before you get to know, how this is done, lets take brief look at how Computer memory looks. Just like in a street, people live in houses, computer memory is organised as a series of cells. The cells do not house people, instead they house data. We can visualise the pic picture of computer memory like the one below :
Suppose we store the number 2 in some memory location. Next time, suppose, we would like to retrieve the contents of this cell. Then how to do that? How do we refer to this cell? The houses in a street have different names. In a similar fashion, what we do is, we give a name to the memory location, say a. And then, we can access the contents of this memory location using the name a. Next time, I simply have to say, Give me the contents of the memory location whose name is location a.. and Ill get the output = 2. Thus, one of the ways to access and manipulate the values(data) of a memory location is to give it a name. If the data stored in a memory location is allowed to change, for example if value in a can be changed to 3, such a memory location is called a Variable We use the Variable. word Variable, because, the contents of such a cell can vary vary. On the other hand, at times, we would like to contents of a cell to remain fixed or constant, not allowing it to change. For example, if a For
12 | P a g e
2008
stores 3.14 the value of Pi, we would like it to be fixed. Such a memory location is called Constant. The name given to a Variable is called a Variable name. The name given to a constant is called Constant Name. In a nutshell, variables and constants provide named access to memory location.
2008
Telling the OS how many bytes of memory you need is called Declaration. While making this declaration/announcement you must specify two important things 1. How many bytes of memory you would need to store your data? 2. What name you would give to this newly reserved memory location, so that you can refer to it, the next time round by this name. As far as the former is concerned, the bytes of memory you would need to store the data really depends on the type of data that you are going to store. A number like 3.14159 needs more memory that just a 3. What type of data you intend to store, can be indicated by specifying the data-type.
2.4 Data-types in C#
Some of the primitive data-types in C# are shown in the table. bool is used to store boolean values just two possibilities 0 or 1 that is true or false. char is used to store single characters like A, B, C, ..., a, b, c,.., 1,2,.. and also, , $, _ and all the letters you would find on the keyboard that have an ASCII value. In C#, char stores the data in Unicode Text Format(UTF). To store integers or numbers without a decimal point, we have four data-types available byte, short, int, long. byte as the name suggests is 1 byte long. So, it can store numbers from 0 to 255. But, what if we wanted to store a larger number like 256. We would then require more than 1 byte of memory space; 2 bytes. So, we use the short data-type. short data-type can store number ranging from -32,768 to +32,767. What if we want to store a still larger number like 50,000? We can then use the int data type(4 bytes) which allows us to store numbers upto 2 billion. If we would like to store a number like a googol, (which is one followed by 100 zeroes), what do you do? You can use a data-type long 8 bytes. To store decimal(real) numbers like 2.5, -3.821 etc. we can use the float and double data-types.
14 | P a g e
2008
Data-type Storage Space Bool 2 bytes Char 1byte Byte 2 bytes Short 4 bytes Int Long Float Double Decimal 8 bytes 4 bytes 8 bytes 8 bytes
Range of Values true,false \u0000 to \uFFFF 0 to 255 -32,768 to +32,767 -2,147,483,648 to +2,147,483,647 -9.22e18 to +9.22e18
Example bool b = true; char ch = A; byte b = 10; short s = 1000; int i = 500; long l = 1000; float f = 3.14F; double d = 25.583
Apart from these, we have sbyte(signed-byte), ushort(unsigned short), uint(unsigned int) and ulong(unsigned long).
15 | P a g e
2008
Declare a variable with the desired data data-type(depending on what data you would like to store)
2.6 Code Snippet How to store and retrieve values from variable
Heres a simple program that illustrates how to declare a variable, assign values to it, and later retrieve the contents of the var variable.
Program that stores and displays the value in a variable 1 using System; 2 3 class VariablesDemo 4 { 5 public static void Main() 6 { 7 /*Declare a bool variable*/ 8 bool b; 9 /*Store a value in it*/ 10 11 b = true; 12 13 /*Display the value in b*/ 14 Console.WriteLine("b = {0}", b); Console.WriteLine( 15 16 /*Declare a character variable*/ 17 char c; 18 19 /*Store a single character in it*/ 20 c = 'M'; 21 /*Display the character stored in c*/ y 22 23 Console.WriteLine("c = {0}", c); Console.WriteLine( 24 25 /*Declare an integer variable*/ 26 int i; 27 28 /*Store an integer number in it*/ 29 i = 10;
16 | P a g e
2008
/*Display the integer value stored in the variable*/ Console.WriteLine("i = {0}", i); /*Declare a float variable*/ float f; /*Store a real number in it*/ f = -0.0125f; /*Display the value of f*/ Console.WriteLine("f = {0}", f); /*Declare a double variable*/ double d; /*Store a large real number in it*/ d = 254.1459; /*Display the value of d*/ Console.WriteLine("d = {0}", d); }
You need to understand, how Console.WriteLine() function works. WriteLine() function is used to display text on the console (command prompt). Whatever is given to WriteLine() in double quotes --- is displayed on the command prompt. The second aspect is the use of placeholders. Notice that weve put {0}s in the WriteLine() function in the above program. {0} is called a place-holder. How it works?
int a = 2, b = 3, c = 4; Console.WriteLine("a = {0},b = {1}, c = {2}", a, b, c);
17 | P a g e
2008
Assume that we take three integer variables a,b and c. Weve put the values 2,3 and 4 in the variables a, b and c respectively. To display their values, we use the WriteLine() function. In the Write WriteLine(), we give a,b and c as arguments without the double quotes. This means . that we want to display the contents of a,b and c. So, the values 2,3 and 4 will be displayed. {0} gets substituted by the value of a 2, {1} by the value of b 3, and the placeholder {2} by the value of the placeholder variable c 4. Output of the above code snippet :
2.6 Operators
In our school and pre-school, we have studied math. Operations like school, adding, subtracting, multiplying, dividing are pretty common. Like in algebra, we have many different operators in C#. ave Class of Operators Binary Arithmetic Operators Unary Arithmetic Operators Logical Operators Relational Operators Bitwise Logical Operators Shift Operators Assignment Operators
+, - ,*,/,% + , - , ++ , -&&, || , ! , ^ <,<=,>,>=,==,!= &,|,~ <<, >> = Shorthand Assignment +=, -= , *= , /= , %= , &=, |= , ^=, <<= , >>=
18 | P a g e
2008
The result of adding a + b, should be 5+10 = 15. So, res contains the value 15. Similarly,
res = b - a; Console.WriteLine("b - a = {0}", res);
To subtract b a and store the result in res, we write the above lines. The effect of this statement would be, 10-5 = 5, and hence res = 5.
res = a * b; //res = 5*10 = 50 Console.WriteLine("a * b = {0}", res); res = b / a; //res = 10/5 = 2 Console.WriteLine("b / a = {0}", res);
Thus, we could write any complex instruction like res = a + b * c d / e. Lets write a simple program that calculates the simple interest.
Program to calculate Simple Interest 01 using System; 02 03 class SimpleInterestDemo 04 { 05 public static void Main()
19 | P a g e
2008
This simple program takes three variables principal, noOfYears, and rateOfInterest, and computes the simple interest by using the formula PNR/100. Upon running the program, we get the following output.
When we divide two numbers, the divide operator / is used to find the answer(quotient) of division. For example, when we divide 11 by 3, we get quotient 1 and remainder 2. Thus, 11/3 = 1(quotient) and 11 modulo 3 = 2(remainder). Modulo operator is used to find the remainder of the division operation.
Program that shows the use of % operator 01 02 03 04 05 06 07 08 09 10 11 12 13 using System; class ModuloDemo { public static void Main() { int a = 11; int b = 3; int remainder = a % b; Console.WriteLine(" 11 % 3 = {0}", remainder); } }
20 | P a g e
2008
Lets study a simple program, that uses the modulo operator % to reverse a four-digit number. For example, if the input number is 1234, the output should be the number 4321.
Program to reverse a 4-digit number 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 using System; class ReverseDemo { public static void Main() { int number = 1234; int int int int u = number % 10; t = (number / 10) % 10; h = (number / 100) % 10; th = (number / 1000) % 10;
int reverse = u * 1000 + t * 100 + h * 10 + th; Console.WriteLine("Original = {0}", number); Console.WriteLine("Reverse = {0}", reverse); } }
The line 5 in the Main() program declares a variable called number, and we store in it the value 1234. To calculate the reverse of a number, the algorithm is as follows a. Input number n b. Extract various digits of the number n. c. Form the reverse number as (Units-digit x 1000) + (Tens-digit x 100) + (Hundreds-digit x 10) + (Thousands-digit x 1) So, we must extract the various digits from one thousand, two hundred and thirty four as 1, 2, 3 and 4. We then form the reverse of this number as 4 x1000 + 3 x 100 + 2 x 10 + 1 x = 4321. Line 14 in the program corresponds to this step.
21 | P a g e
2008
Lines 9 12 are used to extract out the individual digits from the 4digit number. How do you that? Well, it seems weird at first sight. But, here goes. You divide a number like 1234 by 10, and find the remainder. In that case, 1234 modulo 10 = 4. (as 1230 is perfectly divisible by 10; 1234 is 1230 + 4 left over) Thus, number % 10 gives the units digit. Now, to extract the tens digit, we would use the same technique number % 10. However, to extract 3 from 1234, we first divide 1234 by 10. 1234 / 10 = 123 (Truncating 123.4 to 123...) Now, this number 123 % 10 = 3. Thus, we pulled out the tens digit. To pull out hundreds digit(2), 1234 / 100 = 12 12 % 10 = 2. To pull out thousands digit(1), 1234 / 1000 = 1 1 % 10 = 1. Upon running the above program, I get the following output.
2008
result = 2 * 3 + 4; It may be evaluated as result = 2 * 3 + 4 =6+4 = 10 result = 2 * 3 + 4 =2*7 = 14 To resolve the ambiguity, C# assigns a priority to all the operators. (This does not only apply to arithmetic operators, but all kinds operators, however in this book, our discussion is restricted to arithmetic operators) Operators with higher priority are evaluated first and are preferred over operators of lower priority. The priorities of different operators is shown in the table below. This is also called precedence of operators. Priority 1st Priority 2nd Priority 3rd Priority Thus, in the expression result = 2 * 3 + 4 We first evaluate Multiplication *, because it has the highest priority. So, result = 6 + 4 Operators */% += Description Multiplication, Division, Modulo Addition, Subtraction Assignment
23 | P a g e
2008
Next, we evaluate addition +, because + has higher priority over =, and hence + operator precedes over =. result = 10. Lastly, we evaluate the = equals operator. In other words, the value 10 is now stored in the variable result. Now, consider the following expression result = 5 * 4 / 2 ; In this case, * and / have the same priority level. When two operators have the same priority, which operator should be evaluated first? The answer to this question lies in a concept called associativity. Associativity indicated the direction in which the expression is scanned to evaluate the operators. The associativity of arithmetic operators is given in the table below Operators +-*/% = In the expression, result = 5 * 4 / 2 ; Since, = equals operator has R to L associativity, we will first evaluate the right hand side(RHS) and then assign the result to the LHS. The RHS is 5 * 4 / 2. Since, * and / have the same priority, we shall have to consider their associativity. Both * and / have L to R associativity, we start scanning the expression in left to right direction and evaluate the answer. In left to right order, we first encounter *, so 5 * 4 = 20. Associativity Left to Right Associativity Right to Left Associaitivity
24 | P a g e
2008
Then we encounter /. So we evaluate it as, RHS = 5 * 4 / 2 = 20 / 2 = 10. Finally, the result of the expression on the RHS is assigned to the variable result on the LHS. Thus, the value 10 is stored in result.
They all have, some algebraic variables on the RHS, whose result of computation is the LHS. But, you may not see in algebra, an expression like : = +1 =2 = + This might be absurd in terms of algebraic expression, but they are perfectly valid C# arithmetic expressions. In C#, you can have the same variables on the left hand side and right hand side of an arithmetic expression. How to decipher the meaning of the above expression? In an arithmetic expression, as a rule, we always first evaluate the RHS, and the result of the expression on the RHS is assigned to the LHS. Consider = + 1. Initially, suppose the variable a contains 2. 1 + 1 = 2 + 1 = 3.
25 | P a g e
2008
This value 3 is now stored back in the variable a(LHS). Note that, one variable can contain hold one value at a time. So, the old value 2 is lost and it is over-written with the value 3. Thus, this expression increases the value of a by 1. In other word, it increments the value of 1. Similarly, if we would like to decrease the value of a by 1, we would have to write = 1. The expression = 2 doubles the value of a and stores the result in a. For example, to begin with if a = 5, after this statement executes a will hold the value 10.
26 | P a g e
2008
This program takes a variable called a. The variable a is initialised to 2. We increment the value of twice. We then decrement it twice. In each step, we print the result of increment/ decrement to the screen. The output appears as follows :
2008
Lets take simple examples and try to understand the above operators. x = 10 x=8 x=-5 x = 4, y = 3 x=9 x=5 x>5 2*x < 20 -x >= 10 x + y <=9 x + 1 == 10 x!=5 true true false true true false
The Logical AND operator returns a bool value true or false. If both the conditions(operands) are true, the answer is true. The logical AND operator in C# is written as &&. A true true false false B True false true false A && B true false false false
28 | P a g e
2008
The Logical OR operator returns a bool value true or false. Logical OR in C# is written as || (Two vertical bars). The logical OR || returns true even if any one of the condition holds true. A true true false false B True False True False A || B true true true false
The Logical XOR(Exclusive OR) operator also returns a bool value true or false. The logical XOR is a special case of the logical OR operator and is written in C# as ^. Logical XOR returns true only if the conditions(operands) are dissimilar. If they are similar, it returns false. A true true false false B True False True False A^B false true true false
Logical NOT ! operator negates or complements the condition. If the condition evaluates to true, !(condition) would be false. On the other hand, if the condition evaluates to false, !(condition) would be true. Given below is a program that shows how to work with all the logical operators.
Program to show the working of && , || , ^ and ! operators 01 using System; 02 03 class LogicalDemo 04 { 05 public static void Main() 06 { 07 int x = 10; 08 Console.WriteLine("x = {0}", x); 09 10 bool ans = (x > 5) && (x < 15); 11 Console.WriteLine("x > 5 && x < 15 = {0}", ans);
29 | P a g e
2008
ans = (x > 5) && (x > 15); Console.WriteLine("x > 5 && x > 15 = {0}", ans); ans = (x > 5) || (x > 15); Console.WriteLine("x > 5 || x > 15 = {0}", ans); ans = (x == 5) || (x < 0); Console.WriteLine("x == 5 || x < 0 = {0}", ans); ans = (x == 5) ^ (x > 0); Console.WriteLine("x == 5 ^ x > 0 = {0}", ans); ans = (x == 10) ^ (x > 0); Console.WriteLine("x == 10 ^ x > 0 = {0}", ans); ans = !(x > 10); Console.WriteLine("!(x > 10) = {0}", ans); }
Lines 10 and 13 show the && operator. x = 10. So, x>5 = true (as 10 > 5). x < 15 = true (as 10<15) (x > 5) && (x < 15) = true && true = true On the other hand, x > 5 && x > 15 = true && false = false However, Line 16 shows the same two conditions with || operator. x > 5 || x > 15 = true || false = true Line 19 also uses the || operator. This time round, x == 5 is false, since x = 10, and 105. Also, x < 0 is false, since 10 > 0. Thus, x == 5 || x<0 = false || false = false. Lines 22 and 25 show how the exor ^ operator works. In case of line 22, x == 5 is false, x>0 is true and false ^ true = true. In case of line 25, x == 10 is true, x>0 is true and true ^ true = false. Line 27 shows the use of ! operator. Since x > 10 is false (as 10 10), !(x>10) must be true. Upon running the above program, we get the following output.
30 | P a g e
2008
31 | P a g e
2008
6
Polymorphism, Constructors and Destructors
6.1 Function Overloading
There are 6 different versions of the Multiply() method in the Test class. st The 1 version multiplies two ints and returns an int as the answer. nd The 2 version accepts 2 doubles as input parameters and returns a rd double result as the answer. The 3 version accepts an integer and a double number as an argument, and returns their product as the answer. Every time, we are assigning a new extra meaning to the Multiply() function. Thus, we have overloaded the Multiply() method.
class Test { public int Multiply(int a, int b) { return a * b; } public double Multiply(double a, double b) { return a * b; } public double Multiply(int a, double b) { return a * b; } public double Multiply(double a, int b) { return a * b; } public int Multiply(int a, int b, int c) { return a * b * c; } } class OverloadDemo { public static void Main() { Test t = new Test(); double res1 = t.Multiply(4.2, 5.5); int res2 = t.Multiply(3, 5); double res3 = t.Multiply(4.2, 5);
32 | P a g e
2008
In method overloading, we assign a new extra meaning to the method. For example suppose, we have written the Multiply() method as follows : public int Multiply(int x, int y) { int result = x * y; return result; } The Multiply() method is designed to accept 2 integers as input parameters. It multiplies the two integers x and y, and returns an integer result as the answer. Suppose, we now define one more Multiply() method as follows public double Multiple(double x, double y) { double result = x * y; return result; } This Multiply() method multiplies two double numbers and returns a double result as the answer. Thus, the Multiply() method now has two different versions. The first version can multiply two integers, whereas the second version can multiply 2 doubles. Thus, we have assigned a new extra meaning to the Multiply() method, so that it can now no longer multiply just two ints, but it can as well multiply two doubles.Hence, we have overloaded the Multiply() method. Thus, this is method overloading. Let us define one more Multiply() method as follows 33 | P a g e
2008
public double Multiply(int x, double y){ double result; result = x * y; return result; } This time round, the Multiply() method can multiply one int with one double value, and returns a double result as the answer. Thus, we are again assigning a new extra meaning to the Multiply() method. So, once again we have overloaded the Multiply() function. If we define one more Multiply() method as : public double Multiply(double x, int y) { double result; result = x * y; return result; } This new version of the Multiply() method can now multiply a double with an integer. Mutiplying a double with an int, is different from multiplying an int with a double. Thus, we are again assigining a new meaning to the Multiply() method. Hence, we have again overloaded the Multiply() method. Consider another version of the Multiply method public int Multiply(int x, int y, int z){ return x * y * z; } This version of the Multiply() method computes the product of 3 ints instead of 2. Once again we have assigned extra meaning to Multiply(). So, it is an overloaded function. A function is said to be overloaded if 34 | P a g e
2008
1. When two or more methods in the same class have the same name 2. They have different Parameter Lists Parameter list is different if - Type of parameters differs. - No of parameters is different. - Order of parameters is different.
class Date { public int day; public int month; public int year; public static Date operator +(Date d1, Date d2) { Date result = new Date(); result.day = d1.day + d2.day; result.month = d1.month + d2.month; result.year = d1.year + d2.year; if(result.day>30){ result.day -= 30; result.month++; } if (result.month > 12) { result.month -= 12; result.year++; } return result; } public static bool operator <(Date d1, Date d2) { bool ans; int days1 = d1.day + d1.month * 30 + d1.year * 365; int days2 = d2.day + d2.month * 30 + d2.year * 365; if (days1 < days2) ans = true; else ans = false; return ans; } public static bool operator >(Date d1, Date d2)
The + operator is overloaded, so that it can add two Date class objects. The two Date objects are passed as arguments into d1 and d2. To store the sum of Date objects, we take a result Date object. We set the day, month and year of the result Date object as the sum of the corresponding day, month and year of d1 and d2.
We also need to check for overflow. Overflow happens when the no of resulting days exceeds 30. For example, we treat 35 days as 1 month and 5 days. So, when an overflow occurs we add 1 to the months, and the balance days are 35 30 = 5.
The < operator is overloaded, so that it can compare 2 Date objects and find out which is smaller. Comparing 2 Date objects for the no. Of days, months and years can be tricky, so a simple way would be to find the total no of days of each Date object and then just compare them. If days1 turns out to be less than days2 as assumed, return true else return false. We also overload > operator, since they must be overloaded in pairs.
35 | P a g e
2008
36 | P a g e
2008
When the C# system encounters this line, it does not directly add the 2 + 3. Instead, the C# system calls a function with the name + and passes 2 and 3 as the arguments. +(2,3) When this function is called, the control jumps to the function definition. The + function looks like this public int operator +(int a, int b) { ___________________; ___________________; ___________________; } Since, the computer gives us the answer of 2 + 3 = 5, this function + is already defined in C#. Now, suppose we would like to assign extra meaning to the + operator. Let's say we have the following objects MyClass obj1 = new MyClass(); MyClass obj2 = new MyClass(); Suppose, we now write obj1 + obj2 Thus, the following function is called +(obj1,obj2) But, since these our own objects, we have created them, we have made their class design, the + operator does not know how to add these custom made objects. To be able to add them, we need to define the following function -
37 | P a g e
2008
public MyClass operator + (MyClass obj1, MyClass obj2){ MyClass result; //Code for adding the 2 objects.. _______________________________; _______________________________; return result; } By defining this function, the + operator will also be able to add objects. Hence, we are assigning extra meaning to the + operator, so that it is able to add our own objects. Hence, it is called Operator Overloading. Syntax for Overloading Binary Arithmetic Operator public static MyClass operator + (MyClass obj1, MyClass obj2) { MyClass result; ______________ ______________ return result; } Syntax for Overloading Unary Arithmetic Operator public static MyClass operator -(MyClass obj) { MyClass result; ______________ ______________ return result; }
38 | P a g e
2008
Syntax for Overloading Relational Operators public static bool operator <(MyClass obj1, MyClass obj2){ bool ans; //Compare the two objects ______________ ______________ return ans; } Note : Relational operators are always overloaded in pairs. If we overload < operator, then we must also overload > operator. Similarly, if we overload <= or == operator, we must also overload their corresponding complementary operators >= and !=.
6.3 Constructors
class Box { double width; double height; double depth; public Box() { width = height = depth = 1; } public Box(int l) { width = height = depth = l; } public Box(int w, int h, int d) { width = w; height = h; depth = d; } public void GetBox() { Console.WriteLine("w : " + width + " h : " + height + " d : "+depth); }
39 | P a g e
2008
Why Constructors?
When we first create an object, all its fields are initialised to 0. In fields other words, the memory is zeroed out.
To be able to use this Box object, we must put in meaningful values in the width, height and depth of myBox. Thus, we must assign values to myBoxs instance variables. ance To do so, we must manually go in and initialise all the fields of the myBox object.
40 | P a g e
2008
myBox.width = 10; myBox.height = 20; myBox.depth = 30; -rosy All of this seems rosy-rosy till we have to work with just 1 Box object. But, consider a real time shipping application, where we to real-time keep track of thousands of orders and consignments. Each consignment is shipped in Box object. So, if we typically have 1000 Box objects; myBox1, myBox2, myBox3,... this process of manually initialising all the fields becomes very tedious and cumbersome. fields
Well, you can use convenience methods like SetBox(). But, when you have a thousand objects, you have explicitly call/invoke SetBox() method on each Box object. Once again, it calls for a lot of work.
41 | P a g e
2008
Constructor is a special method that is invoked automatically, when an object is created. So, you dont have to call a constructor method explicitly. Thus, constructor can be used to automatically initialise an object, right at the time when the object is born/created. 1. A constructor has the same name as the class. 2. Like methods, constructors can also accept input parameters. 3. Unlike methods, constructors do not have a return type, not even void. 4. Like methods, constructors can also be overloaded. Depending upon the call, different versions of the constructor will be invoked. Note Adding constructors to a class is compulsory. Q. But, the classes that we have written till now seemed to work fine without a constructor. A. If you do not write your own constructor, the C# Compiler adds a constructor method for you for free. Such a constructor is called the default constructor. Q. But how does the default constructor know, what I want it to do? A. It does not! Thats why, it does not accept any parameters and has empty body. It looks like this public Box() { } Thus, the free constructor that C# compiler adds for you, is a donothing constructor.
42 | P a g e
2008
43 | P a g e
2008
A destructor has the same name as the class, but prefixed with a ~ tilde sign. Internally, this Destructor is translated to a call to the Finalize() method
class MyClass { protected override void Finalize() { try { Console.WriteLine("You can free up any unmanaged resources here"); } finally { base.Finalize(); } } }
Now, when you have a number of objects, how does GC keep track of which objects to finalize? The GC maintains a queue of all the objects that are to be finalized. This is called Finalization Queue. However, we never know for sure, when the GC kicks in and calls the finalizer(destructor) method. If there is an unmanaged resource that needs to be released promptly on time, this approach is not helpful. In other words, finalization is non-deterministic(random). When we want to exercise control over the finalization process, we can override the Dispose() method (whose interface is IDisposable). The Dispose() method differs from the Finalize() method, in that, we need to explicitly call the Dispose() method. A finalizer is implicitly called, and cannot be explicitly called, even if you wanted to. Implicit Resource Cleanup Explicit Resource Cleanup
44 | P a g e
2008
7
Inheritance Our objects family tree
7.1 Overview of Inheritance
Kathy is assigned the task of building an Employee Management System. She starts by thinking, I am gonna take Employee objects for every Employee in the organisation. So, she begins by writing an Employee class as follows
class Employee { public string name; public string address; public int SSN; public int number; public float salary; public float computePay() { return salary / 12.0F; } }
The design of an Employee class seems fine to her initially. An employee has a name, address, and number and so on... We want the compute the pay of different Employee objects.
45 | P a g e
2008
But, Kathy questions herself, does every Employee have a salary? Is it true that every Employee object has a salary. By studying the problem domain, she finds out that there are Employees which are paid on an hourly basis, or employees which are on contract basis. The first mistake Kathy made was to add a field of type salary to Employee. She discovers although employees are the objects in our problem domain, there are actually two different types of Employee objects : Salaried Employees and Hourly Employees. Therefore, we should write two classes : Salary and Hourly. The Salary class should have a field to represent the employees annual salary because a Salaried Employee has a salary. The Hourly class should have fields to represent the employees hourly pay rate and the no of hours for which he worked. The Salary and Hourly classes look like this
class Salary { public string name; public string address; public int SSN; public int number; public int salary; public float computePay() { return salary / 12.0F; } } class Hourly { public string name; public string address; public int SSN; public int number; public int hoursWorked; public int hourlyRate; public int computePay() { return hoursWorked*hourlyRate; } }
46 | P a g e
2008
Although Salary and Hourly classes are different types, they are not entirely different. In fact, the two types of employees have a lot in common, as seen by the repetition of fields and methods in these two classes. So, we can take the common elements from both the classes, and put them in a parent class leaving the unique elements behind in the child class. We can simply make the Salary and Hourly classes inherit the elements of the Employee class. Employee class is called Parent class/Base class/Super class. Salary and Hourly classes are called child class/Derived class/subclass. If you want to make Salary and Hourly the child classes of Employee, we write them as follows :
class Employee { public string name; public string address; public int SSN; public int number; } class Salary : Employee { public int salary; public float computePay() { return salary / 12.0F; } } class Hourly : Employee { public int hoursWorked; public int hourlyRate; public int computePay() { return hoursWorked*hourlyRate; } }
When your classes use inheritance, you only need to write your code once.
In the above scenario, Salary class and the Hourly class have a lot of same code.
47 | P a g e
2008
Salary Employee and Hourly Employee are both employees. When you have two classes which are more specific cases of something more general, you can set them up to inherit from the same base class.
Employee name address SSN number GetEmployee()
Build up your class model, by starting General and getting more Specific
48 | P a g e
2008
public void GetEmployee() { Console.WriteLine("Name : " + name); Console.WriteLine("Address : " + address); Console.WriteLine("SSN : " + SSN); Console.WriteLine("Number : " + number); Console.WriteLine(); } } class Salary : Employee { public int salary; public float computePay() { return salary / 12.0F; } public void GetEmployee() { Console.WriteLine("Name : " + name); Console.WriteLine("Address : " + address); Console.WriteLine("SSN : " + SSN); Console.WriteLine("Number : " + number); Console.WriteLine("Salary : " + salary); Console.WriteLine(); } } class Hourly : Employee { public int hoursWorked; public int hourlyRate; public int computePay() { return hoursWorked * hourlyRate; } public void GetEmployee() { Console.WriteLine("Name : " + name); Console.WriteLine("Address : " + address); Console.WriteLine("SSN : " + SSN); Console.WriteLine("Number : " + number); Console.WriteLine("Hours Worked : " + hoursWorked); Console.WriteLine("Hourly Rate : " + hourlyRate); Console.WriteLine(); } } class EmployeeDemo { public static void Main() { Employee e = new Employee(); e.name = "Robert Smith"; e.address = "111 Palm street"; e.SSN = 999901111;
49 | P a g e
2008
50 | P a g e
2008
class Hourly : Employee { public int hoursWorked; public int hourlyRate; public int computePay() { return hoursWorked * hourlyRate; } public void GetEmployee() { base.GetEmployee(); Console.WriteLine("Hours Worked : " + hoursWorked); Console.WriteLine("Hourly Rate : " + hourlyRate); Console.WriteLine(); } } class EmployeeDemo { public static void Main() { Employee e = new Employee(); e.name = "Robert Smith"; e.address = "111 Palm street"; e.SSN = 999901111; e.number = 1; Salary s = new Salary(); s.name = "Jane Smith"; s.address = "Oak Drive"; s.SSN = 111009999; s.number = 2; s.salary = 10000; Hourly h = new Hourly(); h.name = "George Washington"; h.address = "333 Espresso Lane"; h.SSN = 111990000; h.number = 3; h.hoursWorked = 200; h.hourlyRate = 30; e.GetEmployee(); s.GetEmployee(); h.GetEmployee(); } }
51 | P a g e
2008
52 | P a g e
2008
53 | P a g e
2008
Salary s = new Salary("Jane Smith", "222 Oak Drive", 111009999, 3, 10000); s.GetEmployee(); } }
54 | P a g e
2008
8
Advanced C# Language Concepts
8.1 Using Parent class references to Child Objects
An object reference can refer to an object of its class, or to an object of any class derived from it by Inheritance. Suppose, we have the following class hierarchy.
For example, if Animal class is the parent, and Bird is its child, SongBird is the child of Bird and so on,... Animal reference can refer to a Bird object or a SongBird object. Animal a; a = new Animal();
55 | P a g e
2008
a = new Bird(); a = new SongBird(); Assigning an object to an ancestor reference is considered to be a widening conversion, and can be performed using simple assignment. Animal a = new MockingBird(); Assigning an ancestor object to a child reference can also be done, but it is considered to be a narrowing conversion and must be done with a cast. MockingBird m = (MockingBird)new Bird(); The widening conversion is the most useful for implementing polymorphism.
56 | P a g e
2008
57 | P a g e
2008
58 | P a g e
2008
59 | P a g e
2008
An abstract method is never called. Think about it. If computePay() is abstract in the Employee() class, we really dont care what computePay() does in the Employee class, because it will never be invoked/called. So, the abstract computePay() method in the Employee class will be empty. Our assumption is that the child classes of Employee will override the computePay(). This is where abstract methods are useful. If you want a class to contain a particular method, but you want the actual implementation to be determined by the child classes, you must declare the method in the parent class as abstract. Abstract methods consist of a method signature, but no method body.
public abstract class Employee { public string name; public string address; public int SSN; public int number; public abstract void computePay(); }
8.5 Namespaces
Every class belongs to a namespace. Namespaces basically have two purposes 1. Namespace provides a mechanism for organising classes. 2. Namespace does compartmentalization. When developing a C# program, you put classes that go together in the same namespace. For example, in C# the classes that are used to
60 | P a g e
2008
perform basic Input and Output are in System namespace. The classes used for GUI applications are in System.Windows.Forms namespace. The classes used for creating threads are in System.Threading namespace. Q. Why are namespaces necessary? What if I have a small program that is only a dozen classes? Namespace is more than just a mechanism for organising classes. A more important aspect of namespaces is that they create compartments. For example, suppose that your program contains a class named Vehicle. What if I wrote a class Vehicle as well? How would you be able to tell them apart. What if someone wanted to use my Vehicle class and your Vehicle class in their program? Q. I have seen this problem before. Why dont you change the names of the classes, such as Vehicle1 and Vehicle2? No thanks. I would have to re-write and re-compile a bunch of code. With namespaces, I dont have to worry about it. If the two Vehicle classes are in different namespace/compartments, my C# program can distinguish between the two Vehicle classes.
61 | P a g e
2008
The Employee, Salary and Hourly classes are now all in the same namespace.
The Employee class can now no longer be accessed by the name Employee. To refer to the Employee class, we must everytime call it payroll.Employee. Similarly, we must refer to Salary and Hourly classes as payroll.Salary and payroll.Hourly. Suppose you have a written a Vehicle class, and I too have written a Vehicle class. Kate wants to use both the Vehicle classes in their program. So, I put my Vehicle class in a compartment quasar, and you put your vehicle class in another compartment student. Kate will now refer to both the Vehicle classes as,
62 | P a g e
2008
quasar.Vehicle student.Vehicle To create objects of these classes, Kate would write quasar.Vehicle v1 = new quasar.Vehicle(); student.Vehicle v2 = new student.Vehicle(); Here, v1 refers to a quasar.Vehicle object, whereas v2 refers to student.Vehicle object. Thus, from the above example, you can deduce the fact that, namespaces help you to avoid naming conflicts by creating compartments. Note Classes within the same namespace/compartment do not need to use the <namespace>.<class-name> convention while referring to each other. Thus, if write another class Test inside payroll namespace, we can call Employee, Salary and Hourly classes directly without using any special naming convention. Classes in the same compartment find each other without any special syntax.
63 | P a g e
2008
There is another class Boss which would like create Employee objects.
using System;
class Boss { public static void Main() { payroll.Employee e = new payroll.Employee("Jane Smith", "111 Oak drive", 999001111, 1); } }
Writing payroll.Employee everytime could become tedious or cumbersome. To make things easy, we can use the using keyword. using keyword specifies in which namespace to look for the given classes. If you write using payroll, then we can refer to the Employee class, simply as Employee without using the fully-qualified name.
using System; using payroll; class Boss { public static void Main() { Employee e = new Employee("Jane Smith", "111 Oak street", 999001111, 1); } }
Note that, using keyword is just a convenience statement. During compilation, the C# compiler will replace all instances of Employee class with payroll.Employee(Fully-Qualified name).
64 | P a g e
2008
File1.cs
File2.cs
File3.cs
namespace A { .... }
namespace B { .... }
namespace C { .... }
8.10 Interfaces
An interface is a collection of abstract members. A class implements an interface thereby inheriting the abstract methods of the i interface. All the methods of an interface need to be defined in the class. Syntactically, an interface is defined using the C# interface keyword.
public interface IName { void method1(); void method2(); }
In the .NET Framework, interfaces have a special naming convention. All interface names begin with a capital I. Interfaces can have properties as well. Difference from a class 1. You cannot create an object of an interface.
65 | P a g e
2008
2. An interface does not contain any constructor. 3. All methods in an interface are abstract. 4. An interface can inherit multiple interfaces. An interface is like a contract a promise that a class will implement a specific set of functionalities. When a class implements an interface, it signs a contract, a treaty and promises to provide implementation/definition of all methods or functions declared in the interface. C# code can question an object to determine whether it supports an interface. Interrogating an object is basically asking the question,Do you support this interface?. If the object answers, Yes, I do!, than you can call the methods defined in the interface on the object.
66 | P a g e
2008
public double GetWeeklyPay() { return weeklyPay; } public void computePay(int hoursWorked) { weeklyPay = hoursWorked * 6.50; Console.WriteLine("Weekly Pay for : " + name + " is Rs." + weeklyPay); } public void MailCheck() { Console.WriteLine("Mailing check to : " + name + " at " + address); } }
An organisation has many employees working in it. The Employee of an organisation is represented by an Employee class object. Associated with every Employee is his personal information such as his name, address. Every employee also has a salary and other payrelated information. The organisation has two departments Payroll and Human Resource. Different parts in an organisation have different data needs. For example, the payroll department handles the payroll needs, but it does not need access to or change the personal information of an employee. On the same lines, the Human Resource department manages the general information about Employees, but it does not need access to the Employees pay details. How do we realise these business rules in C#? The answer lies in the innovative use of Interfaces. Although we cannot instantiate(create an object) an interface, we can do the following
67 | P a g e
2008
interface MyInterface { void a(); void b(); } class MyClass : MyInterface { void a(){ Console.WriteLine(Inside A); } void b(){ Console.WriteLine(Inside B); } void c(){ Console.WriteLine(Inside C); } } Declare an interface reference MyInterface iref; Assign to it, an object of a class that implements this interface iref = new MyClass(); Using iref, we can call the methods a() and b(), but we cannot call the method c(). Thus, we can call only those methods on the object, which are declared and exposed by the interface. Thus, by assigning an object to an interface reference, we can restrict access to the methods that can be called on the object. In the Employee class example, we could take two different interfaces payable and EmployeeInfo for the Payroll and HR departments. The payable interface exposes and provides an interface to only those
68 | P a g e
2008
methods that are needed by the payroll department. Through the EmployeeInfo interface, the HR department can only access those methods which help it to manage Employee Information like name and address.
public interface Payable { void computePay(int hoursWorked); void MailCheck(); double GetWeeklyPay(); } public interface EmployeeInfo { string GetName(); void SetName(string name); string GetAddress(); void SetAddress(string address); }
To represent the Payroll and Human Resource Departments, we write the Payroll and HumanResource classes.
public class Payroll { public void PayEmployee(Payable p) { p.computePay(10); p.MailCheck(); } } public class HumanResource { public string GetInfo(EmployeeInfo e) { return e.GetName() + " " + e.GetAddress(); } public void ChangeName(EmployeeInfo e, string n) { Console.WriteLine("Changing Name for : " + e.GetName()); e.SetName(n); Console.WriteLine("New name is : " + e.GetName()); } public void UpdateAddress(EmployeeInfo e, string a) { Console.WriteLine("Changing address for : " + e.GetName()); e.SetAddress(a); Console.WriteLine("New address is :" + e.GetAddress()); } }
69 | P a g e
2008
Now, we write the Main Program, where we take a fictious Employee, and the payroll and HR departments.
class EmployeeManagement { public static void Main() { Employee e = new Employee("George Washington","Mt. Vernon"); Payroll payroll = new Payroll(); HumanResource hr = new HumanResource(); payroll.PayEmployee(e); hr.GetInfo(e); hr.ChangeName(e, "Bill Gates"); hr.UpdateAddress(e, "Redmond VA"); } }
70 | P a g e
2008
9
File Reading and Writing I/O
9.1 Concept of Streams
Many applications which you would write, need to store data and retrieve data from a file. For example, in an Employee Management System, you would like the Employee details to be stored permanently in a file on the disk. You would also want to read Employee data from the file on disk. You might also need to update an employees information in a file, say for example his salary is raised, or his marital status changes from single to Married. Reading or writing data to files is a functionality which you require often while making your application. Thus, it is important that we study, how we can read or write to files. However, an application cannot directly read or write data to a file. To connect an application to a file, we must use a virtual pipe. Just as a real pipe carries water, the virtual pipe carries bytes of data to and fro between the application and the file. This virtual pipe in C# is called a Stream. If the stream connects to a file, its called FileStream.
71 | P a g e
2008
To create a FileStream in C#, we must create an object of the ream FileStream class. FileStream fs = new FileStream(<file FileStream(<file-name>,<file-mode>,<file mode>,<fileaccess>,<file-Share>) The FileStream constructor accepts the FileName as the first argument. This must be the absolute file path. N Next, the file mode must be specified. The file mode tells the C# system, which mode you would like to operate the file in. The FileMode enum looks like this enum FileMode{ CreateNew, Create, Open, OpenOrCreate, Truncate, Append } Create always creates a new file. CreateNew create a new file, and if the file already exists, it overwrites the old file. OpenOrCreate works in 2 ways if the file does not exist, it creates the file, if the file exists
72 | P a g e
2008
it will open the file. Open simply opens the file. Append is used to append whatever you write/delete to(from) the end of the file. FileAccess enum specifies whether you would like to read from the file, write to the file or do both ; ReadWrite. A FileStream attached to a file, is all you need to do operations like adding data, modifying data and deleting data from a file. To write to a FileStream, we can call its Write() method. Before reading from the stream, we must reset the internal position of the stream(using Position property) and call the ReadByte() method. Directly reading and writing bytes to a file can become tedious as it demands that you work directly with bytes. Reader and Writer objects encapsulate(hide from you) the detailed process of reading and writing, and provide a higher level of abstraction to you.
73 | P a g e
2008
The above program asks the user to enter the path, where he would like to store the file. It then asks the user to enter some text to be written to the file. A FileStream object is created and hooked to the file in the specified path, with the Create FileMode and Write access(as we want to write text to the file). A writer object is created. There is a class StreamWriter in C# that represents the concept of writer. To model a writer, we must create an object of the StreamWriter class. While creating a StreamWriter object, we must pass as an argument to the constructor, the stream, we would like the StreamWriter object to write to. The writer object is now the tool that you would use to actually write things to a file. You do so by calling the methods of the StreamWriter object. To simply write text, we use Write() method. WriteLine() method always write the text onto a new line each time. To the Write() and WriteLine() methods you can pass the text that you would like to write as a String object. After youre done with writing text to the file, it is very important to remember that we must unhook the FileStream from the file. If you forget to do this, even after the program is over, the file will remain blocked by the FileStream, so no other program which needs access to the file will be able to access it. To do so, we close the FileStream and StreamWriter objects, by calling their Close() methods.
74 | P a g e
2008
To read from a file, one must first create a FileStream object and hook it upto the file, open the file in read-mode. Next, we take a StreamReader object and pass to its constructor the stream, we would like to read from. We read one line at a time, by calling ReadLine() method on the reader object. Thus, the entire file is read line-by-line till we do not encounter the end of the file, where the input read is null. Finally, we close the StreamReader object.
75 | P a g e
2008
FileStream stream = new FileStream("C:\\abc.txt", FileMode.Open, FileAccess.Read); StreamReader reader = new StreamReader(stream); string input = null; stream.Seek(12, SeekOrigin.Begin); while ((input = reader.ReadLine()) != null) Console.WriteLine(input); reader.Close(); } }
Given the above abc.txt file, we get the following output, when we specify the offset = 12 bytes from the beginning.
The output is still the same, SeekOrigin.Current move the cursor ahead by 2 bytes from its current position. After the first call to Seek() the cursor is at position 10. Thus, current position of cursor becomes 10. After the second call to Seek(), the cursor would reach 10+2=12th position.
76 | P a g e
2008
The DirectoryInfo class is used in a similar manner to find information about a directory.
using System; using System.IO; class DirectoryDemo { public static void Main() { DirectoryInfo dir = new DirectoryInfo("C:\\"); Console.WriteLine("Directory Name : " + dir.FullName); Console.WriteLine("Last Access Time : " + dir.LastAccessTime); Console.WriteLine("Creation Time : " + dir.CreationTime); Console.WriteLine("Attributes : " + dir.Attributes); } }
77 | P a g e
2008
78 | P a g e
2008
79 | P a g e
2008
10
Threads, Processes, AppDomains and Multi-threading
80 | P a g e