.Net Architecture and C# Programming

grapedraughtΛογισμικό & κατασκευή λογ/κού

2 Δεκ 2013 (πριν από 3 χρόνια και 8 μήνες)

86 εμφανίσεις


3

.Net Architecture and C# Programming

.Net architecture provides a common class library and a safe execution environment for
developing and executing object
-
oriented programs in any .Net language. The main
architecture of .Net platform is shown below.






















.Net programming can be carried out in any .Net compliant language. Current choices
include twenty seven different programming languages, the most popular being C#. Other
languages include VB.Net, J#.Net, Jscript.Net and ma
naged C++. A .Net program is first
compiled to an IL (intermediate language) code called MSIL, which is similar in concept
to the Java bytecode. The CLR then translates it to the target machine code by a just in
time (JIT) compilation process. So technical
ly speaking, a .Net program can execute on
any operating system (OS) if the proper CLR exists. .Net provides full language
independence and integration because of a common class library which is often referred
to as the Framework Class Library(FCL). A clas
s developed in one .Net language can be
derived from a class written in another .Net language.


Of all the .Net language, C# is considered to be the most powerful language as it has been
elegantly designed by borrowing some of the best features of C++, Jav
a, Visual Basic
and Delphi languages. Syntactically, C# is similar to C++ and Java languages. Some of
its important principles such as automatic garbage collection, translating the code into an
intermediate language before execution, providing a CLR at run

time for safe execution
of programs, are very similar to Java language. However, unlike Java, C# is always
ultimately compiled into machine language, supports operator overloading, and allows
interaction with direct memory access (pointers) through an
uns
afe

block mechanism.

Common Language Runtime

(debugging, exception handling,
garbage collection, Just in
Time
Compilation)

Common Framework Class
Library (FCL)

(Framework base classes, I/O, security,
reflection, ADO.Net, SQL, XML)

Windows Forms

(any .Net language)

Web Forms

(any .Net language)

Web Services

(any .Net language)

Machine
Code

Assembly in In
termediate Language (MSIL)
Code

Compiler


4

Important Features of C#:



C# is influenced by Java, Visual Basic, C++ and Delphi. It contains the rapid GUI
development features of Visual Basic, operator overloading from C++, and many
features from Java.



All classes derive from Syste
m.Object.



System.Object provides the
ToString

method which is overridden in a derived
class. This method is automatically called when trying to print the value of the
object of a class.



Everything is an object in C#, including the primitive data types such

as
int, float,
char etc..
An
int

is actually an alias for System.Int32 class. Similarly,
float

is an
alias for System.Single. string is an alias for the System.String class. double is an
alias for System.Double class.



All intrinsic data types such as byte,

bool, int, short, long, float, double etc.. are
termed as
value

types in C#.
struct

is also a value type in C#. Value type objects
are created on the stack. User defined objects (i.e., objects of user defined classes)
are created on the heap using the
new

operator.



struct is more efficient than a class from memory and execution time point of
view. A struct cannot inherit from a class or another struct. A struct can
implement an interface.



CLR provides automatic garbage collection (similar to Java), so that

you as a
programmer do not have to worry about memory leaks.



char

data type in C# contains Unicode characters similar to Java.



A larger size data cannot be assigned to a smaller size variable without explicit
type casting. For example:

short s1;

int a = 2
5;

s1 = a ; // error, proper way is : s1 = (short) a;


Similarly a decimal literal is a double and cannot be assigned to a float
type variable, e.g.,

float f1 = 2.35; // error, correct way is: float f1 = 2.35f;



The expression inside an
if

statement must ev
aluate to a true or false i.e., bool
type, otherwise it is a compiler error in C#. For example,

if (a = 5) will cause a compiler error as the return from a = 5 is 5.



The switch statement in C# needs an explicit fall through e.g.,

switch (a) {

case 5 :
a = 22; goto case 6; // compiler error without goto

case 6: b = b


1; break;

default: b = 10; break;


}



The switch statement in C# can compare strings.



goto, continue, break and return statements are available in C#. continue causes
the iteration to go b
ack to the start of the loop.



for each

loop has been added to C# to iterate on arrays and collections (similar to
Visual Basic)


5



C# supports preprocessor with #define, #undef, #if , #else, #endif, #region, and
#endregion statements. The region statements ar
e used by the Visual Studio editor
to collapse regions i.e., sections of code (specially for wizard generated code and
XML style comment blocks).



C# classes do not have to end with a semicolon as in C++.



C# classes uses access modifiers as
public
,
private
,

protected
,
internal

and
protected internal
.
internal

means that the method is visible to any method of a
class in the assembly but not outside of the assembly.
protected internal
means
that the method is visible to either a derived class or to any class i
nside the
assembly (think of
protected internal

as protected or internal).



Data members and member functions in a class are private by default. A method
in a C# class needs to specify its access modifier (i.e., private, public etc..) if it is
other than th
e default (similar to Java).



C# supports only single inheritance of classes, but allows multiple interface
inheritance (similar to Java).



An interface is a collection of abstract functions (pure virtual in terms of
equivalent C++ functions).



Classes in C#
do not inherit constructors. You have to explicitly call the base
class constructor in a derived class using the
base

keyword e.g., base(lname,ID);

Note that a default constructor for a base class does not need to be called
explicitly. It will be implicitl
y called. You can also use the base keyword to call
any method in the base class e.g., base.f1( );



There are no header files in C#.



C# uses namespaces to group related types, similar to C++. Unlike C++, the using
keyword can only be applied to namespaces a
nd not to classes. Namespaces can
be viewed as somewhat equivalent to packages in Java where
using
in C# would
be the equivalent of
import

statement in Java.



Just like a java application is packed into a
jar

file, a C# application is packed into
an
assembl
y
. An assembly is a collection of public types, internal types and meta
data describing the contents of the assembly. The compiled code in the assembly
is in IL format which is just
-
in
-
time compiled when the program executes.



C# uses // as well as /* */ t
ype of comments.



C# uses Pascal convention for naming methods and the camel convention for
naming the variables, i.e., all class and method names start out with a capital letter
and the start of each following word in the class name or method name is also
capitalized. However, the names of data members and local variables start out
with a lower case letter.



C# types include classes, enums, structs and delegates.



If a class has a static method in it, this method can be invoked without having to
create an ob
ject of the class (similar to Java).



CLR calls the Main() method (which is declared static) in a program to start its
execution. The Main method can return int or void. It is possible to have multiple
classes with a Main method. However, in this case, you
should use the /main
compiler switch to indicate which class’s main to use when starting the execution
of the program.


6



C# programs are compiled into assemblies. An assembly is a collection of files
that appears as a single
dll

or exe file. It provides the
basic unit of reuse,
versioning, security and deployment. The CLR provides a number of classes for
manipulating assemblies.



You can compile a program from the command line as:

csc filename.cs



To run the program, simply type filename



C# supports component

based programming features in the form of properties,
events, and declarative attributes. The CLR stores each class’s meta data
(equivalent of type library) with the IL code for the class, and the serialization
code if the class is serializable. Using ref
lection, the class meta data can be read.



C# allows accessing memory directly using C++ style pointers. Such operations
are marked as unsafe, so that the CLR does not attempt to collect objects pointed
to by pointers until the pointers are released.



Built
in data types in C#:

Alias

.Net Class


Range

byte

System.Byte


(0
-
255), sbyte

System.Sbyte (
-
128 to 127)

char

System.Char


(Unicode)

bool

System.Boolean

(true or false)

short

System.Int16


(16 bits)

ushort

(unsigned short)

int

System.Int32


(32

bits)

uint (unsigned int)

long

System.Int64


(64 bits)

ulong (unsigned long)

float

System.Single


(32 bits)

double

System.Double

(64 bits)

decimal System.Decimal

28 digits, for financial calculations,





requires suffix m or M


Integer values are not imp
licitly converted to Boolean type



You can use constants in C# similar to C++, e.g.,

const float pi = 3.141517f;



You can use escape characters in C#, similar to C++, e.g.,

\
\

for a single backslash,
\
n,
\
r,
\
t,
\
’ for a single quote,
\
” for a double quote..



A variable must be assigned a value before you can use it in an expression, or
print it, or assign it to another variable.

Example:


int a;


int b = 5;


System.Console.WriteLine(“ a = {0}, b = {1}”, a, b);



The above program will generate a compiler err
or because a is not
initialized and you are trying to print its value..



Enumerations have the general form as:

[attributes] [modifiers]
enum

identifier [:base type]

{


enumeration list

}




7

Example:



enum Colors




{




Red = 1,




Green = 2,




Blue =
3




}


An explicit conversion to a type is required before assigning the enumerated
constant to a variable.

In your code, you can use the above defined enumeration as,



int myColor = (int) Colors.Red;




enum Greet : int

{




Hello,

// will have a va
lue 0




Hi,

// will have a value 1




Bye = 5,




Adios

// will have a value 6



}





If you are creating a class and wanted to use the enumeration inside it, you can
declare the enumeration and use it as shown below.


class xyz {



enum Sizes : int





{




Small = 2,




Medium = 5,




Large = 8,




ExtraLarge = 10




}



static void Main()



{




System.Console.WriteLine(“ Large Size = {0}”,Sizes.Large);



}


}




8

Visual Studio .Net


Creating a simple C# Console Program:

Run Visual Studio .Net. From
the file menu, choose new project. Give the project name
“First” as shown below: Make sure you choose the project type to be console, also select
visual C# Projects. You may want to create a special directory for your C# projects.



After you choose OK, t
he wizard will generate the following code for you.



You can modify the above code to as shown below. Notice that the name of the class has
been changed to
Welcome.

Also, this class will be stored in a file called Welcome.cs. You
can change the file name

by selecting the Class1.cs on the right hand side in the solution
explorer, then changing the file name to Welcome.cs.


9


From the Build menu, you can now build the project. Then you can run it by choosing
Debug
-
>Start without debugging.

Visual Studio .Net

comes with an excellent debugger similar to Visual Studio 6. You can
modify the program to as shown below. Then set a break point by simply clicking in the
bar area on the left hand side. Then you can choose debug
-
> start and step through the
program by h
itting F10.



10

Creating Classes in C#:


Example
: Create a console application called “MyTime”. Change the name of the class
to Time. Type the following code:

using System;


namespace MyTime

{


/// <summary>


/// Summary description for Time.


/// </summary>


public class Time


{



/// <summary>



/// The main entry point for the application.



/// </summary>



int Year;



int Month;



int Date;



int Hour;



int Minute;



int Second;






public Time()



{ // constructor




System.DateTime dtm = System.Date
Time.Now; // current date and time




this.Year = dtm.Year;




this.Month = dtm.Month;




this.Date = dtm.Day;




this.Hour = dtm.Hour;




this.Minute = dtm.Minute;




this.Second = dtm.Second;



}




public Time(System.DateTime dtm)



{ // constructor




this.Year = dtm.Year;




this.Month = dtm.Month;




this.Date = dtm.Day;




this.Hour = dtm.Hour;




this.Minute = dtm.Minute;




this.Second = dtm.Second;



}




public void PrintDate()



{




Console.WriteLine("Date={0}/{1}/{2}",Month,Date,Year);



}




public void PrintTime()



{




Console.WriteLine("Time={0}:{1}:{2}",Hour,Minute,Second);



}



11



public void PrintDateAndTime()



{




Console.WriteLine("{0}/{1}/{2} {3}:{4}:{5}",





Month,Date,Year, Hour,Minute,Second);



}


}// end of class Time



c
lass Test1 { // tester class for the Time class




static void Main(string[] args)



{




//




// TODO: Add code to start application here




//




Time t1 = new Time();




t1.PrintDate();




t1.PrintTime();




t1.PrintDateAndTime();



}


}

}


Run the pr
ogram by choosing, Debug
-
>Start without Debugging. The output will look
something like:




Exercise
: Modify the Main program so that it initializes the T1 object by calling the
constructor that takes an object of DateTime class instead of the empty constr
uctor.


C# does provide a default constructor which initializes all data members to their default
values i.e., integers to zero, char to ‘
\
0’, bool to false etc..


Exercise
: Modify the above program to include a copy constructor, as shown below.


public Ti
me (Time rhs) {



Year = rhs.Year;



Month = rhs.Month;



Date = rhs.Date;



Hour = rhs.Hour;



Minute = rhs.Minute;



Second = rhs.Second;

}



You can invoke the above copy constructor by modifying the code in the Main as:


12

static

void

Main(
string
[] args)

{

DateTime dt = DateTime.Now; // get current date and time

Time t1 =
new

Time(dt);

Time t2 = new Time(t1) ; // invoke copy constructor, compare to C++



t2.PrintDate();



t2.PrintTime();

t2.PrintDateAndTime();

}



System.Object Class:


All classes in C# ar
e derived from System.Object class. Object class provides six
methods as shown below. Many of these objects are overridden by derived classes to
provide a correct implementation (ToString in particular).



Method



Explanation

Equals



Determines if two
objects are equal

GetHashCode


Hash code identifying an object (useful in collections).

GetType


Returns the type of the object

ToString


String representing the state of the Object

Finalize


For cleanup of resources

MemberwiseClone

To create copies of the

object.



Formatting Output:

When you use the WriteLine method, e.g.,


Console.WriteLine(“ a = {0:[FormatCharacter[Number]]}”,a);

Optionally, you can specify a formatting character and a number indicating the
appropriate formatting field.

Formatting Chara
cter


Effect



C or c



Currency, adds a $ sign and rounds to two decimal places

D or d



Decimal numbers e.g., D7 will give 7 spaces for the number

E or e



Exponent notation i.e., Scientific form

F or f



For floating point numbers, e.g., F3 will yield
3 decimal places

N or n



Basic Numeric formatting with commas.

X or x



Hexadecimal format.


Example: The following code segment produces output as shown below:





int

n1 = 987;




float

f1 = 2.5467f;




Console.WriteLine("n1 = {0:D7}, f1={1:F2}",n1,f1)
;




Console.WriteLine("n1 = {0:X}, f1={1:e}",n1,f1);


13



Passing Parameters:


C# passes value types
by value
, and objects
by ref
erence by default. However, a
ref

keyword is provided so that value types can be passed by reference. Further, an
out

keyword is

provided so that those ref parameters that are not initialized can be passed
successfully to a function.


Example:



public

void

exchange(
ref

int

p,
ref

int

q)



{ // added as a member function to tester class




int

temp;




temp = p;




p = q;




q = t
emp;



}


//
----
partial code from main method

tester test =
new

tester();

int

l, m;

l = 5; m = 7;

Console.WriteLine("Before Exchange L = {0}, M = {1}",l,m);

test.exchange(
ref

l,
ref

m);

Console.WriteLine("After Exchange L = {0}, M = {1}",l,m);


Now suppose
the exchange function has a third parameter which the exchange function
will modify to be the sum of the first two parameters. In this case, it does not make sense
to explicitly initialize the third parameter before calling the function, i.e., the call mig
ht
look as:

int n;




test.exchange(
ref

l,
ref

m, n);


However, the above code will generate compiler error, as n has not been initialized. The
solution to this is the out parameter. The correct code is shown below.



public

void

exchange(
ref

int

p,
ref

int

q,
out

int

r)



{




int

temp;




temp = p;




p = q;




q = temp;




r = p + q;



}


14

//
---
partial code from Main method

tester test =
new

tester();

int

l, m;

l = 5; m = 7;

int

n;

Console.WriteLine("Before Exchange L = {0}, M = {1}",l,m);

test.exchange(
ref

l,
ref

m,
out

n);

Console.WriteLine("After Exchange L = {0}, M = {1}, N =
{2}",l,m,n);



Variable Number of Parameters:


C# allows the last parameter of a method to be prefixed by the “params” keyword.
This indicates that the method can accept multiple val
ues for this parameter of the same
type.


Example:




Welcome w1 =
new

Welcome();




float

a1 = 2.5f;




float

a2 = 4.8f;




Console.WriteLine("Average = {0}",w1.ComputeAvg(2,a1,a2));



}




public

float

ComputeAvg(
int

size,
params

float
[] vals
)



{




flo
at

sum = 0;




for

(
int

i = 0; i < size; i++)





sum = sum + vals[i];




return

sum/size;



}



Static Members and Methods:


C# does not allow standalone functions, or global variables. One way to achieve a
global function’s equivalent would be through st
atic member functions. To invoke a
static member function, you do not need to create an object of the class. You simply
invoke it through the class name.

Example: If a class called XYZ has a static function called f1 in it. Then you will invoke
the f1 func
tion as:

XYZ.f1( );


NOTE: Static member functions cannot access nonstatic members.


Example: Add a class called XYZ to an existing project (by right clicking on the project
name and choosing add new class). The code is shown below.


using

System;

namespac
e

OperatorOverload


15

{


///

<summary>


///

Summary description for XYZ.


///

</summary>


public

class

XYZ


{



int

p, q;




public

XYZ()



{




p = 5; q = 7;



}



public

static

void

f1()
// static member cannot access non static
member



{




//Console.Writ
eLine("p = {0}, q = {0}",p, q); // will not work




Console.WriteLine("XYZ static f1 called");



}


}

}


Now from the tester class’s main method, you can invoke the XYZ static function f1 as,

XYZ.f1( );



Static Constructors and Static data members:


A sta
tic constructor is executed only once before any object of the class it belongs
is instantiated. Static constructors can be used to provide an initialization of an
environment for all objects.

A common use of a static data member is to provide a count for

how many objects
of a class has been created. Note that a static data member is shared between all objects
of a class.


Example:
Modify the code in the XYZ.cs file to include a static constructor and a static
data member as shown below.

using

System;


nam
espace

OperatorOverload

{


///

<summary>


///

Summary description for XYZ.


///

</summary>


public

class

XYZ


{



int

p, q;



static

int

Count;




static

XYZ()
// static constructor called once in the beginning



{




Count=100;



}




public

XYZ()
// no
rmal constructor


16



{




p = 5;




q = 7;




Count++;



}






public

static

void

f1()
// static member cannot access non static
member



{




//Console.WriteLine("p = {0}, q = {0}",p, q); // will not work




Console.WriteLine("XYZ static f1 called, Count="

+ Count);



}


}

}


You can modify the code in the main to invoke the XYZ methods to as shown below:




XYZ.f1(); // will print 100




XYZ xyz =
new

XYZ();




XYZ.f1(); // will print 101


Note that, often we write static methods to provide access to p
rivate static data members.



Finalize Method:

C# does not have destructors as objects allocated on the heap are automatically
freed by the garbage collector once their reference count goes to zero. However, in some
cases, if the object is holding to some
unmanaged resources (e.g., file handles), we need
to write a Finalize method in our class to free such resources. The Finalize method is
called by the Garbage collector just before it frees the object.

Note that you should not try to call the Finalize met
hod yourself. In C#, you write
the Finalize method by actually writing as destructor, e.g., the Finalize method for the
XYZ class will look as,


~XYZ()
// equivalent of Finalize method



{
// automatically calls base.Finalize




Console.WriteLine("Finalize

called ");


}

C# does not allow you to override the Finalize method in the System.Object class.
Even though there is no code in the Finalize method of the Object class, you effectively
override it by creating a destructor.


There are situations in which y
ou may want to release the resources yourself as
early as possible. In such situations, your class should implement the IDisposable
interface. This interface has only one method called Dispose in which you will free the
resources acquired and suppress the
calling of the Finalize method. For example, the
XYZ class would be modified as,


public

class

XYZ : IDisposable





public

void

Dispose()


17



{




Console.WriteLine("Disposing resources ");




// release the resources




GC.SuppressFinalize(
this
);
// no nee
d for GC to call
Finalize now

}


The code in the main that invokes the Dispose method would look like:




XYZ xyz =
new

XYZ();




XYZ.f1();




xyz.Dispose();


Using Statement for Early Disposing of Objects:


Instead of having the client call Dispose method

explicitly, C# provides an
alternative using statement. The object on which you would like to invoke the Dispose
method is created and its use is marked inside a using statement block e.g.,


XYZ xyz = new XYZ();


using (xyz) {



//
---

use the xyz object


} // now Dispose will be automatically called for xyz here

The biggest advantage of the using statement is that even in case of exceptions, the
Dispose method is guaranteed to be invoked.



Operator Overloading
:
C# supports operator overloading somewhat
similar to
C++. All operators are declared
static

so binary operator functions take two parameters
instead of one. C# requires you to overload some operators in pairs i.e., both = = and !=
should be overloaded. Similarly if you overload >, you have to ove
rload < operator as
well. Further C# recommends that if you overload the = = operator, you should also
override the
Equals

member function (override it from the base class Object). Since some
.Net languages do not provide operator overloading (e.g., VB.Net
), it is a good practice to
provide alternative functions to the operators you are overloading e.g., if you overload +,
provide a member function called Add, that does the same thing.



Example:

Create a console C# application. Name the project “OperatorOv
erload”. By
right clicking on the project, add a class to the project called x. Type the following code
in it:

//
-------------------
x.cs
----------------------------------

using

System;


namespace

OperatorOverload

{


///

<summary>


///

Summary description f
or x.


///

</summary>


public

class

x


18


{



int

a, b;



public

x()



{




a = 0; b = 0;



}



public

x (
int

a1,
int

b1)
// another constructor



{




a = a1; b = b1;



}




public

override

string

ToString()



{




string

s = " a=" + a + " b=" + b;




ret
urn

s;



}




public

static

x
operator

+ (x lhs, x rhs)
// objects are passed
by reference



{




x temp =
new

x(); // compare this to C++ approach




temp.a = lhs.a + rhs.a;




temp.b = lhs.b + rhs.b;




return

temp;



}



public

x Add(x rhs)
// alternat
e method for .Net languages



{


// that do not support overloading operators




x temp =
new

x();




temp.a =
this
.a + rhs.a;




temp.b =
this
.b + rhs.b;




return

temp;



}




public

static

bool

operator

== (x lhs, x rhs)



{




if

((lhs.a == rhs.a) && (
lhs.b == rhs.b))





return

true
;




else





return

false
;



}





public

override

bool

Equals (
object

rhs)



{




if

(! (rhs
is

x))





return

false
;




else





{





if

((
this
.a == ((x)rhs).a) && (
this
.b == ((x)rhs).b))






return

true
;





else






return

false
;




}



}




public

static

bool

operator

!= (x lhs, x rhs)



{




if

((lhs.a != rhs.a) || (lhs.b != rhs.b))


19





return

true
;




else





return

false
;



}


}

}


Change the name of the default class, class1 to tester. Type the following code in

the
tester class.


//
-----------------
tester.cs
---------------------------------

using

System;


namespace

OperatorOverload

{


///

<summary>


///

Summary description for Class1.


///

</summary>


class

tester


{



///

<summary>



///

The main entry point f
or the application.



///

</summary>



[STAThread]



static

void

Main(
string
[] args)



{




x x1 =
new

x(2,3);




x x2 =
new

x(3,5);




x x3 =
new

x();




x3 = x1 +x2;




x3 = x1.Add(x3);




if

(x1 == x2)





Console.WriteLine("x1 and x2 are equal");




el
se





Console.WriteLine("no they are not equal");




Console.WriteLine(x3.ToString());




Console.WriteLine(x1.GetHashCode());




Console.WriteLine(x2.GetHashCode());



}


}

}


Build and run the above program by selecting “Start Without Debugging” from th
e
Debug menu.






20

Properties in C#:


C# has a new construct called properties for allowing controlled access to data
members of a class. In C#, you declare a property just like you declare a data member,
but instead of writing an explicit get function and

set function, you write get and set
accessors tied to this property.


public

class

x


{



private

int

a, b;



public

int

A



{




get





{





return

a;




}




set




{





a =
value
;




}



}




public

int

B



{




get




{





return

b;




}




set




{





b = (
int
)
value
;




}



}

Now someone can create an object of the class x, and use the properties as:

X x1 = new x();

x1.A=5; x1.B=7;

Console.WriteLine(“x1.a = {0}, x1.b = {1}”,x1.A, x1.B);



ReadOnly Fields:


C# provides a readonly keyword that is

typically used with static data members of a
class. The idea is that the static constructor would initialize these static data members and
then the readonly keyword will prevent the user from modifying its value.

Example:


public static readonly int ID;





21


Overriding Inherited Members


override

and
new

keywords:


C# requires (actually recommends by compiler warnings) an explicit keyword called
override

when overriding a method in a derived class. If your intention is simply to
declare a completely new me
thod in the derived class, then you use the
new

keyword
with the method. This greatly helps in alleviating the versioning problems in case of base
and derived classes.

Example:


class Circle : Shape {







public
override

void ComputeArea( ){ // overrides

the base class function




….



}




public
new

void DrawDotted( ) { // now if some one added a DarwDotted




….// function to base class, code will not break



}



Example: Create a Console type application called OverrideNew. Add a class called
CBase t
o it with the the following code.

//
----------
CBase.cs
-------------

using

System;

namespace

OverrideNew

{


///

<summary>


///

Summary description for CBase.


///

</summary>


public

class

CBase


{



public

CBase()



{



}



public

virtual

void

f1()
// virtu
al is important in order for base class



{



// references to successfully call f1 in derived class




Console.WriteLine("F1 in Base Class");



}


}

}


Add another class to the project called CDerived with the following code:

//
--------
CDerived.cs
--------
------

using

System;


namespace

OverrideNew

{


22


///

<summary>


///

Summary description for CDerived.


///

</summary>


public

class

CDerived : CBase


{



public

CDerived()



{



}



public

void

f1()
//compiler generates warning without override



{




Conso
le.WriteLine("F1 in Derived class");



}




public

void

f2()
// compiler generates warning without new



{




//
when f2 is added to the base class




Console.WriteLine("F2 in Derived
-

new function added");



}


}

}

Modify the default class1 by changing i
ts name to Tester. The code in tester will look as:

//
--------
Tester.cs
-----------

using

System;


//
----------
Tester.cs
--------------

namespace

OverrideNew

{


///

<summary>


///

Summary description for Class1.


///

</summary>


class

Tester


{



///

<summar
y>



///

The main entry point for the application.



///

</summary>



[STAThread]



static

void

Main(
string
[] args)



{




CBase b1 =
new

CDerived();




b1.f1();





if

(b1
is

CDerived
)




{





CDerived d1 = (CDerived) b1;





d1.f2();
// works OK unt
il some one adds f2 to base class




}
// now compiler generates a warning message



}


}

}


23


If you compile and run the above program, it will produce the output as:



However, if you modify the base class by adding the f2 function as shown be
low:

Add the following function to the CBase class.



public

virtual

void

f2( ) {




Console.WriteLine("F2 in Base Class");



}


Now the compiler will generate a warning message as shown below.


d:
\
csharpprogs
\
overridenew
\
cderived.cs(18,15): warning CS0108
: The keyword new is
required on 'OverrideNew.CDerived.f2()' because it hides inherited member
'OverrideNew.CBase.f2()'

To fix this warning, you can modify the f2 function in CDerived.cs as:



public

new

void

f2()
// compiler generates warning without new



{




//
when f2 is added to the base class




Console.WriteLine("F2 in Derived
-

new function added");



}



Calling Base Class Functions and Constructors:


In C#, classes do not inherit constructors, so a derived class has to explicitly call a
base clas
s constructor e.g., the CDerived constructor might look as:


CDerived(int m, int n ) {



base(m,n );

// call base class constructor



// do some more initialization


}

The default constructor in the base class does not need to be called explicitly. Note al
so
that if you write a constructor in a class, the default constructor is not provided by the
compiler. In this case, you need to write a default constructor as well in most situations.

If there is a method in the base class that you need to invoke, you ca
n do it by using the
base keyword e.g.,
base.f1( );



Abstract Methods and Abstract Classes:


If it does not make sense to provide implementation for a method in a class, it can be
marked as abstract. If a class contains one or more abstract methods, it al
so needs to be
marked as abstract e.g.,


abstract

public class Shape {



protected int x1;



protected int y1;


24



protected int x2;



protected int y2;




Shape(int x1, int y1, int x2, int y2) {




this.x1 = x1;




this.y1 = y1;




this.x2 = x2;




this.y2
= y2;



}



abstract

public void Draw( );


}



public class Rectangle : Shape{



protected int fillColor;




Rectangle(int x1, int y1, int x2, int y2, int fillColor) {




base(x1,y1,x2,y2);




this.fillColor = 2;



}



public override void Draw( ) {




//
drawing code for the rectangle



}


}


It is not possible to create an object of an abstract class. A derived class can implement
the abstract method(s) using the override keyword. If the derived class does not
implement the abstract method, then it too be
comes an abstract class and thus cannot be
instantiated.


Sealed Classes:


A class can be marked as sealed indicating that it cannot be derived from e.g.,



sealed
public class X {




// data members and methods



}


Now if some one tried to derive a class

from Z as:


class MyX : X {



// …


}

Compiler will generate an error message.

As an example, the Garbage Collector class (System.GC class) in the .Net framework is
declared as a sealed class.





25

Boxing and UnBoxing Types:


Boxing refers to converting a v
alue type to an object type, and unboxing refers to the
opposite. Boxing is carried out implicitly in C# whereas you have to use type casting to
unbox to an appropriate data type.

Example of Boxing:


int i = 5;


Console.WriteLine(“ i = {0}”, i);


WriteLine

method requires an object, so in the above statement integer i is implicitly
boxed to an object and passed to the WriteLine method.


Example of Unboxing:


int i = 5;


object obj = i; // boxing is implicit



int j;


j = (int) obj ; // to unbox, you have
to type cast.


Typically, unboxing is done in a try block. If the object being unboxed is null or if the
unboxing cannot succeed because the object is of a different type, an
InvalidCastException is thrown.




Arrays and Collections:


There are many useful

collections in the .Net framework, such as Array, ArrayList,
Queue, Stack, Hashtable, BitArray etc..


The simplest of the above collections is an Array. The System.Array class class provides
many useful methods such as:

Copy( )
-





copies one section of

an array to another.

Sort( )






sorts the values in a 1
-
D array.

BinarySearch( )
-



searches a 1
-
D sorted array.

Reverse( )
-



reverses the order of elements in a 1
-
D array.

IndexOf( )
-




returns the index of first occurance of a value in 1
-
D arra
y.

Length
-




returns length of the array.


To declare and instantiate an array, you have to use the new operator as,


int [] myArray = new int[5];


The default value for integer array elements is 0.

Alternatively, you can declare and initialize the array

at the same time as,


int [] myArray = { 3, 5, 7, 9, 11} ;


To traverse an array, you can either use the for loop as,

For (int i =0; i < myArray.Length; i++)


26


sum = sum + myArray[i];

Or you can use the
foreach

statement as:


foreach(int val in myArray)



sum = sum + val;


Multidimensional Arrays:


You can declare a two dimensional array in C# as:


float [,] Matrix = new float[rows, columns];


then you access the 2
-
D array as:


for (int i = 0; i< rows; i++)



for (int j = 0; j < columns; j++)




Matrix[i,j]

= i*j;


C# also supports
jagged

arrays where the rows in a multi
-
dimensional array can be of
different size.



Indexers:


C# does not allow you to overload the [] operator, but has another mechanism known
as the
indexer

that achieves the same goal. The in
dexer is declared as a property in a
class, and you provide the set get methods of this property to decide how the [] operator
will work.


Example: Suppose you created a class in a project called
Student

whose code looks as:

//
------
Student.cs
----------

us
ing

System;


namespace

First

{


public

class

Student


{



private

int

id;



public

int

ID



{




get

{
return

id; }




set

{ id =
value
; }



}






private

string

lname;



public

string

Lname



{




get

{
return

lname; }




set

{ lname =
value
; }



}



27



pu
blic

Student(
int

id,
string

lnm)



{




this
.id = id;




this
.lname = lnm;



}


}

}


Suppose you also created another class called University which can have an array of
Student objects in it. In this class, we will declare an indexer on the student ID so t
hat a
client can simply use the university object as an array of students. The indexer code is
shown in bold below.

//
-------------
University.cs
--------------

using

System;


namespace

First

{


public

class

University


{



private

string

uname;



public

str
ing

Uname



{




get

{
return

uname; }




set

{ uname =
value
; }



}




private

Student [] stds;




public

University()



{




// initialize the three students




this
.uname = "University of Bridgeport";




stds =
new

Student[3];




stds[0] =
new

Student(1
00, "Baker");
// IDs start at 100




stds[1] =
new

Student(101, "Harris");




stds[2] =
new

Student(102, "Mahmood");



}




public

Student
this
[
int

stid]
// creating indexer



{




get





{





if

(((stid
-
100) > 0) && ((stid
-
100) >= stds.Length))





{







Console.WriteLine("incorrect ID range");






return

null
;





}


28





else






return

stds[stid
-
100];




}




set




{





if

(((stid
-
100) > 0) && ((stid
-
100) >= stds.Length))





{






Console.WriteLine("incorrect ID range");






return
;





}





else






stds[stid
-
100] = (Student)
value
;




}



}


}

}


The nice thing about an indexer is that you can use strings or other objects inside the [] as
long as you have provided (overloaded) the proper indexer inside the class. For example,
if we wanted to in
dex a University object on “Lname”, we can add the following indexer
to the University class.



public

Student
this
[
string

lnm]
//creating another indexer



{




get





{





bool

found =
false
;





int

i;





for

(i = 0; i < stds.Length; i++)





{






if

(stds[i].Lname == lnm)






{







found =
true
;







break
;






}





}





if

(found ==
true
)






return

stds[i];





else






{






Console.WriteLine("No student exists with this
name {0}",lnm);






return

null
;





}




}




set




{





bool

found =
false
;


29





int

i;





for

(i = 0; i < stds.Length; i++)





{






if

(stds[i].Lname == lnm)






{







found =
true
;







break
;






}





}





if

(found ==
true
)






stds[i] = (Student)
value
;





else






{






Console.WriteLine("No stu
dent exists with this
name {0}", lnm);






return
;





}




}



}


The client code that invokes the above two types of indexers in the University class will
look as:




University u1 =
new

University();




u1.Uname = "Boston University";








// trying
the ID indexer




Console.WriteLine("Student ID 100 Last Name={0}",





u1[100].Lname);




u1[101].Lname = "Harrison";




Console.WriteLine("Student ID 101 New Last Name={0}",





u1[101].Lname);








// trying the other lname indexer




Console.WriteLin
e("Student Harrison ID = {0}",





u1["Harrison"].ID);


If the above code was typed in the Main method of a console application, then when you
run the program, the output will appear as:






30

Hashtables:


Hashtable class implements the hash table data stru
cture where you can store a key,
value pair. The key will be placed in the Hashtable based on Hash code. If you recall, the
Object class provides a virtual method called “GetHashCode( )” which you can override
to provide your own hash code. When you insert

a key,value pair in the Hashtable class
object by calling the Add( ) method, it calls GetHashCode( ) on the key specified.
Similarly, when you try to retrieve a value from the Hashtable, it calls the GetHashCode(
) to try to access the appropriate bucket.

The key in a Hashtable can be a standard type or
a user defined type. If you are using a user defined type as the key, then you must
implement the GetHashCode( ) as well as the Equals method.


Hashtable class implements the IDictionary interfacewhich prov
ides a property called
Item. The Item property retrieves a value with the specified key. The Item property is
implemented as an indexer, so you will access the Hashtable like an array when looking
up information..


Example:

You will need to expose the Syst
em.Collections namespace to be able to use Hashtable
class.


using

System.Collections;

Suppose the following code was typed inside the Main method of a console application,


//
----------
Hashtable example
----------




Hashtable htbl =
new

Hashtable();




h
tbl.Add(100,"Baker");
// ID, Lname




htbl.Add(101,"Harris");




htbl.Add(102,"Mahmood");





Console.WriteLine("Last Name for Student 101: {0}",





htbl[101].ToString());


Sometimes you need totraverse the entire Hashtable. This can be done by obtaining
the
IDictionaryEnumerator. Like other Enumerators, it provides the MoveNext( ) method
which gives you an access to the next bucket in the Hashtable.





IDictionaryEnumerator enmr = htbl.GetEnumerator();




while
(enmr.MoveNext())




{





Console.WriteLine
("key:{0}, value:{1}",






enmr.Key.ToString(), enmr.Value.ToString());




}

When you run the program, the output will look like: