5.1 Write your own class
In CSharp we can write our own class. Object-oriented programming is essentially
programming in terms of smaller units called objects. An object oriented program is
composed of one or more objects. Each object holds some data (fields or attributes)
as defined by its class. The class also defines a set of functions (also called methods
or operations) which can be invoked on its objects. Generally the data is hidden
within the objects (instances) and can be accessed only through functions defined
by its class (encapsulation). One or more objects (instances) can be created from
a class by a process called instantiation. The process of deciding which attributes
and operations will be supported by an object (i.e. defining the class) is called
abstraction. We say the state of the object is defined by the attributes it supports
and its behaviour is defined by the operations it implements. The term passing a
message to an object means invoking its operation. Sometimes the set of operations
supported by an object is also referred to as the interface exposed by this object.
C# also introduces properties. Properties act like methods to the creator of the
class but look like fields to the client of the class.
Polymorphism refers to the ability of objects to use different types without regard
to the details.
Constructors are methods that are invoked when objects are instantiated. The CLR
defines one, but it is better practice to code the constructors that are required. All
objects are created using new.
A Default Constructor Generated by compiler if none exists. They have the same
name as the class, no return type, no arguments are required, all fields are initialized
to zero, have public accessibility. Note that writing any constructor stops default
creation.
A Private Constructor prevents unwanted objects from being created. Instance
63
64 CHAPTER 5. OBJECT-ORIENTED PROGRAMMING
methods cannot be called. Static methods can be called. Commonly used to implement
procedural functions.
A Static Constructor is called by class loader at run time. It is used to initialize
static members and is guaranteed to be called before instance constructor. It cannot
have parameters. Use this to declare an access modifier.
A default constructor may look like the following example.
public Person1() { }
The following program shows an example for class Person. The Person is described
by his name, age and sex. Thus we have three attributes:
myName, myAge, mySex.
These private data members are only visible inside the body of class Person1.
// Person1.cs
using System;
class Person1
{
private string myName = "N/A";
private int myAge = 0;
private char mySex = ’n’;
// default constructor
public Person1() { }
// constructor
public Person1(string name,int age,char sex)
{
this.myName = name;
this.myAge = age;
this.mySex = sex;
}
// declare a Name property of type string
public string Name
{
get { return myName; }
set { myName = value; }
}
5.1. WRITE YOUR OWN CLASS 65
public int Age
{
get { return myAge; }
set { myAge = value; }
}
public char Sex
{
get { return mySex; }
set { mySex = value; }
}
public override string ToString()
{
return "Name = " + Name + ", Age = " + Age + ", Sex = " + Sex;
}
public override bool Equals(object o)
{
if((myName==((Person1) o).myName) &&
(myAge==((Person1) o).myAge) &&
(mySex==((Person1) o).mySex))
return true;
else return false;
}
public static void Main()
{
// create a new Person object using default constructor
Person1 person1 = new Person1();
// Set some values on the person object
person1.Name = "Joe";
person1.Age = 99;
person1.Sex = ’m’;
Console.WriteLine("Person details - {0}",person1);
Person1 person2 = new Person1();
person2.Name = "Jane";
person2.Age = 31;
person2.Sex = ’f’;
Console.WriteLine("Person details - {0}",person2);
Person1 person3 = new Person1();
person3.Name = "Jane";
66 CHAPTER 5. OBJECT-ORIENTED PROGRAMMING
person3.Age = 31;
person3.Sex = ’f’;
Console.WriteLine("Person details - {0}",person3);
bool same = person2.Equals(person3);
Console.WriteLine("same person = " + same);
Person1 person4 = new Person1("otto",42,’m’);
Console.WriteLine("Person details: " + person4.myName);
Console.WriteLine("Person details: " + person4.mySex);
// array of persons
Person1[] pmany = new Person1[2];
pmany[0] = new Person1("Carl",23,’m’);
pmany[1] = new Person1("Ola",7,’f’);
Console.WriteLine("name of person[0]: " + pmany[0].myName);
Console.WriteLine("sex of person[1]: " + pmany[1].mySex);
} // end Main
}
class Person1 defines three private member variables, myName, myAge, mySex.
These variable are visible only inside the body of class Person1. To access these
variable outside the class we use the special property methods Name, Age, and Sex.
The CSharp compiler translates for example the Name property to a pair of methods,
namely
public string get_Name()
{ return myName; }
public void set_Name(string value)
{ myName = value; }
The class Person1 contains the Main() method. In some application it would be
better to keep the class Person1 without the Main() method and have an extra file
which calls the Person objects. The file Person.cs only contains the class Person
and no Main() method.
// Person.cs
using System;
class Person
{
public string myName = "N/A";
public int myAge = 0;
public char mySex = ’n’;
5.1. WRITE YOUR OWN CLASS 67
// default constructor
public Person() { }
// constructor
public Person(string name,int age,char sex)
{
this.myName = name;
this.myAge = age;
this.mySex = sex;
}
// declare a Name property of type string
public string Name
{
get { return myName; }
set { myName = value; }
}
public int Age
{
get { return myAge; }
set { myAge = value; }
}
public char Sex
{
get { return mySex; }
set { mySex = value; }
}
public override string ToString()
{
return "Name = " + Name + ", Age = " + Age + ", Sex = " + Sex;
}
public override bool Equals(object o)
{
if((myName==((Person) o).myName) &&
(myAge==((Person) o).myAge) &&
(mySex==((Person) o).mySex))
return true;
else return false;
}
}
68 CHAPTER 5. OBJECT-ORIENTED PROGRAMMING
To generate the Person.dll file we run
csc /t:library Person.cs
The file PersonMain.cs contains the Main() method.
// PersonMain.cs
using System;
class PersonMain
{
public static void Main()
{
// create a new Person object using default constructor
Person person1 = new Person();
// Set some values on the person object
person1.Name = "Joe";
person1.Age = 99;
person1.Sex = ’m’;
Console.WriteLine("Person details - {0}",person1);
Person person2 = new Person();
person2.Name = "Jane";
person2.Age = 31;
person2.Sex = ’f’;
Console.WriteLine("Person details - {0}",person2);
Person person3 = new Person();
person3.Name = "Jane";
person3.Age = 31;
person3.Sex = ’f’;
Console.WriteLine("Person details - {0}",person3);
bool same = person2.Equals(person3);
Console.WriteLine("same person = " + same);
// increment the age of person3
person3.Age++;
Console.WriteLine("person3 new age: " + person3.Age);
Person person4 = new Person("otto",42,’m’);
Console.WriteLine("Person details: " + person4.myName);
Console.WriteLine("Person details: " + person4.mySex);
5.2. OVERRIDE METHODS 69
// array of persons
Person[] pmany = new Person[2];
pmany[0] = new Person("Carl",23,’m’);
pmany[1] = new Person("Ola",7,’f’);
Console.WriteLine("name of person[0]: " + pmany[0].myName);
Console.WriteLine("sex of person[1]: " + pmany[1].mySex);
} // end Main
}
To get the PersonMain.exe file we run
csc /r:Person.dll PersonMain.cs
However, we can also use the short-cut
csc Person.cs PersonMain.cs
to generate the PersonMain.exe file.
5.2 Override Methods
When we write our own class in most cases we should override the methods
string ToString(), bool Equals(), object Clone()
An example is given below
// Point3D.cs
using System;
public class Point3D
{
public double X;
public double Y;
public double Z;
public Point3D() { } // default constructor
public Point3D(double x,double y,double z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
70 CHAPTER 5. OBJECT-ORIENTED PROGRAMMING
// square of distance between two Points
public double distance(Point3D p1,Point3D p2)
{
return (p1.X-p2.X)*(p1.X-p2.X)+(p1.Y-p2.Y)*(p1.Y-p2.Y)+(p1.Z-p2.Z)*(p1.Z-p2.Z);
}
public override string ToString()
{
return String.Format("({0},{1},{2})",X,Y,Z);
}
public override bool Equals(object o)
{
if((X==((Point3D) o).X) && (Y==((Point3D) o).Y) && (Z==((Point3D) o).Z))
return true;
else return false;
}
public object Clone()
{
object o = new Point3D(X,Y,Z);
return o;
}
}
An application of this class is
// Point3DMain.cs
using System;
public class Point3DMain
{
public static void Main()
{
double x = 2.1;
double y = 3.1;
double z = 4.5;
Point3D p1 = new Point3D(x,y,z);
Console.WriteLine("p1 = " + p1);
double a = 2.1;
double b = 3.1;
double c = 4.5;
Point3D p2 = new Point3D(a,b,c);
5.3. INHERITANCE 71
bool r = p1.Equals(p2);
Console.WriteLine("r = " + r);
double k = 2.0;
double m = 1.0;
double n = 7.5;
Point3D p3 = new Point3D(k,m,n);
Point3D p4 = new Point3D(0.0,0.0,0.0);
p4 = (Point3D) p3.Clone(); // type conversion
Console.WriteLine("p4 = " + p4);
Point3D q = new Point3D();
double d = q.distance(p1,p3);
Console.WriteLine("d = " + d);
}
}
5.3 Inheritance
Inheritance is one of the primary concepts of object-oriented programming. It
allows us to reuse existing code. In the next program we use inheritence. The
class Car is derived from class Vehicle. In the class Car we use code from the
class Vehicle. First we must declare our intention to use Vehicle as the base
class of Car. This is accomplished through the Car class declaration
public class Car : Vehicle
Then the colon, : , and the keyword base call the base class constructor. C# supports
single class inheritance only.
A sealed class protects against inheritence.
An abstract class can only be used as a base class of other classes. They cannot be
instantiated and may contain abstract methods and accessors. It is not possible to
modify an abstract class with the sealed modifier, thus the class cannot be inherited.
// MVehicle.cs
using System;
class Vehicle
{
private int weight;
private int topSpeed;
72 CHAPTER 5. OBJECT-ORIENTED PROGRAMMING
private double price;
public Vehicle() {}
public Vehicle(int aWeight,int aTopSpeed,double aPrice)
{
weight = aWeight;
topSpeed = aTopSpeed;
price = aPrice;
}
public int getWeight() { return weight;}
public int getTopSpeed() { return topSpeed; }
public double getPrice() { return price; }
public virtual void print()
{
Console.WriteLine("Weight: {0} kg",weight);
Console.WriteLine("Top Speed: {0} km/h",topSpeed);
Console.WriteLine("Price: {0} Dollar",price);
}
} // end class Vehicle
class Car : Vehicle
{
private int numberCylinders;
private int horsepower;
private int displacement;
public Car() { }
public Car(int aWeight,int aTopSpeed,double aPrice,int aNumberCylinders,
int aHorsepower,int aDisplacement) : base(aWeight,aTopSpeed,aPrice)
{
numberCylinders = aNumberCylinders;
horsepower = aHorsepower;
displacement = aDisplacement;
}
public int getNumberCylinders() { return numberCylinders; }
public int getHorsepower() { return horsepower; }
public int getDisplacement() { return displacement; }
5.3. INHERITANCE 73
public override void print()
{
base.print();
Console.WriteLine("Cylinders: {0} ",numberCylinders);
Console.WriteLine("Horsepower: {0} ",horsepower);
Console.WriteLine("Diplacement: {0} cubic cm",displacement);
}
}
class myCar
{
public static void Main()
{
Vehicle aVehicle = new Vehicle(15000,120,30000.00);
Console.Write("A vehicle: ");
aVehicle.print();
Console.WriteLine("");
Car aCar = new Car(3500,100,12000.00,6,120,300);
Console.Write("A car: ");
aCar.print();
Console.WriteLine("");
}
}
An interface looks like a class, but has no implementation. The only thing it contains
are definitions of events, indexers, methods, and/or properties. The reason interfaces
only provide definitions is because they are inherited by classes and structs, which
must provide an implementation for each interface member defined. Since interfaces
must be defined by inheriting classes and structs, they must define a contract. The
following program shows an example.
// Policies.cs
public interface Policy
{
double Rate(double principal,int period);
}
public class BronzePolicy : Policy
{
public double Rate(double p,int n)
{ return 7; }
}
public class SilverPolicy : Policy
74 CHAPTER 5. OBJECT-ORIENTED PROGRAMMING
{
public double Rate(double p,int n)
{ return (p < 25000.0) ? 8 : 10; }
}
public class GoldPolicy
{
public double Rate(double p,int n)
{ return (n < 5) ? 9 : 11; }
}
public class PlatinumPolicy : Policy
{
public double Rate(double p,int n)
{
double r = (p < 50000.0) ? 10 : 12;
if(n >= 3) r += 1;
return r;
}
}
Each policy provides a Rate method which returns the rate of interest for a given
principal and for a given period. All policy classes implement the Policy interface
except GoldPolicy which defines Rate without implementing policy. We compile
Policies.cs to create Policies.dll via
csc /t:library Policies.cs
Using reflection we can also create an instance of a class and invoke its member
methods at runtime. This feature can be used to write more generic (dynamically
extensible) programs which receive names of classes at runtime.
The investment.cs program including Main accepts principal, period and policy
name as arguments args[0], args[1], and args[2]. We compile investment.cs
as
csc /r:Policies.dll investment.cs
We would execute the program, for example,
investment 60000 5 SilverPolicy,Policies
// investment.cs
using System;
using System.Reflection;
5.4. OVERLOADING METHODS 75
class Investment
{
public static void Main(string[] args)
{
double p = Double.Parse(args[0]); // string to double
int n = Int32.Parse(args[1]); // string to int
Type t = Type.GetType(args[2]);
Policy pol = (Policy) Activator.CreateInstance(t);
double r = pol.Rate(p,n);
double amount = p*Math.Pow(1.0+r/100.0,n);
Console.WriteLine("you will get {0:#.00}",amount);
}
}
5.4 Overloading Methods
Methods in a class can be overloaded. The methods of a class may have the same
name
if they have different numbers of parameters, or
if they have different parameter types, or
if they have different parameter kinds.
We overload the method max() to find the maximum of numbers.
// methods.cs
using System;
public class Maximizer
{
public class Maximum
{
public static double max(double p,double q)
{
if(p > q) return p;
return q;
}
public static double max(double p,double q,double r)
{
return max(max(p,q),r);
}
public static double max(double[] list)
76 CHAPTER 5. OBJECT-ORIENTED PROGRAMMING
{
if(list.Length == 0) return 0;
double max = list[0];
foreach(double val in list)
{
if(val > max) max = val;
}
return max;
}
public static void Main(string[] args)
{
Console.WriteLine("maximum of 4.5 and 4.7 is {0}",max(4.5,4.7));
Console.WriteLine("maximum of 3.1, 2.4, 4.9 is {0}",max(3.1,2.4,4.9));
double[] array = new double[4];
array[0] = 3.1; array[1] = 5.7; array[2] = 3.9; array[3] = 2.1;
Console.WriteLine("maximum element in array = {0}",max(array));
} // end main
}
}
Exercise. Add a method max(int,int,int,int) to the program that find the
maximum of four integer numbers.
5.5 Operator Overloading
Operators can be overloaded (operator overloading) such that
+, -, *, \, %, []
In the next program we overload + to add two complex numbers.
// Complex.cs
using System;
public class Complex
{
public double real; // real part of complex number
public double imag; // imaginary part of number
public Complex(double real,double imag)
{
this.real = real;
this.imag = imag;
5.6. STRUCTURES AND ENUMERATIONS 77
}
public static Complex operator + (Complex c1,Complex c2)
{
c1.real = c1.real + c2.real;
c1.imag = c1.imag + c2.imag;
return c1;
}
}
class ComplexMain
{
public static void Main()
{
Complex r1 = new Complex(1.0,2.0);
Complex r2 = new Complex(2.0,1.0);
r1 = r1 + r2;
Console.WriteLine("Real: {0} Imaginary: {1}i",r1.real,r1.imag);
}
}
Exercise. Overload in the program also * for the multiplication of two complex
numbers. The multiplication of two complex numbers z1 = a + ib and z2 = c + id
(with a, b, c, d real) is given by z1 ¤ z2 = a ¤ c − b ¤ d + i(ad + bc).
5.6 Structures and Enumerations
In CSharp we can also use structures. Structures, or structs, contain properties,
methods, fields, operators, nested types, indexers and structors. Structs do not support
inheritence or destructors. The objects are stored on the stack compared to
classes which are stored on the heap. Structs are useful for small data structures
such as complex numbers, key-value pairs in a dictionary or points in a coordinate
system. They should only be used for types that are small and simple.
Enumerations are an alternative to constants when grouping related constants together.
For example, when considering temperature we can have the following.
enum Temp
{
AbsoluteZero = 0,
CosmicBackground = 2.75,
WaterFreexingPoint = 273,
WaterBoilingPoint = 373,
}
78 CHAPTER 5. OBJECT-ORIENTED PROGRAMMING
The base type for enums is integer. The following example demonstrates how to use
different types.
enum NumberChocolatesInBox :uint
{
SmallBox = 20,
MediumBox = 30,
LargeBox = 50,
}
The program shows how structs and enumeration can be used.
// enumeration.cs
using System;
enum MemberType { Lions, Elefant, Jackals, Eagles, Dogs };
struct ClubMember
{
public string Name;
public int Age;
public MemberType Group;
}
class enumeration
{
public static void Main(string[] args)
{
ClubMember a; // Value types are automatically initialized
a.Name = "John";
a.Age = 13;
a.Group = MemberType.Eagles;
ClubMember b = a; // new copy of a is assigned to b
b.Age = 17; // a.Age remains 13
Console.WriteLine("Member {0} is {1} year old and belongs to group of {2}",
a.Name,a.Age,a.Group);
} // end main
}
5.7 Delegates
A delegate in C# allows us to pass methods of one class to objects of other classes
that can call those methods. We can pass method m in class A, wrapped in a dele5.7.
DELEGATES 79
gate, to class B and class B will be able to call method m in class A. We can pass
both static and instance methods. This concept is familiar to C++ where one uses
function pointers to pass functions as parameters to other methods in the same
class or in another class. C# delegates are implemented in the .NET framework as
a class derived from System.Delegate. Thus the use of delegates involves four steps.
1. Declare a delegate object with a signature that exactly matches the method
signature that we are trying to encapsulate.
2. Define all the methods whose signature match the signature of the delegate object
that we have defined in step 1.
3. Create delegate object and plug in the methods that we want to encapsulate.
4. Call the encapsulated methods through the delegate object.
The following C# program shows the above four steps.
// Delegate.cs
using System;
// Step 1. Declare a delegate with the signature of the
// encapsulated method
public delegate void MyDelegate(string input);
// Step 2. Define methods that match with the signature
// of delegate declaration
class Class1
{
public void delegateMethod1(string input)
{
Console.WriteLine("delegateMethod1: the input to the method is {0}",input);
}
public void delegateMethod2(string input)
{
Console.WriteLine("delegateMethod2: the input to the method is {0}",input);
}
} // end class Class1
// Step 3. Create delegate object and plug in the methods
class Class2
{
public MyDelegate createDelegate()
{
Class1 c2 = new Class1();
80 CHAPTER 5. OBJECT-ORIENTED PROGRAMMING
MyDelegate d1 = new MyDelegate(c2.delegateMethod1);
MyDelegate d2 = new MyDelegate(c2.delegateMethod2);
MyDelegate d3 = d1 + d2;
return d3;
}
} // end class Class2
// Step 4. Call the encapsulated method through the delegate
class Class3
{
public void callDelegate(MyDelegate d,string input)
{
d(input);
}
} // end class Class3
class Driver
{
public static void Main()
{
Class2 c2 = new Class2();
MyDelegate d = c2.createDelegate();
Class3 c3 = new Class3();
c3.callDelegate(d,"calling the delegate");
} // end Main
}
The output is:
delegateMethod1: the input to the method is calling the delegate
delegateMethod2: the input to the method is calling the delegate
Another example is given below. Here we use delegates to compare strings.
// MyDelegate.cs
using System;
// this is the delegate declaration
public delegate int Comparer(object obj1,object obj2);
public class Name
{
public string FirstName = null;
public string LastName = null;
5.7. DELEGATES 81
public Name(string first,string last)
{
FirstName = first;
LastName = last;
}
public static int CompareFirstNames(object name1,object name2)
{
string n1 = ((Name) name1).FirstName; // type conversion
string n2 = ((Name) name2).FirstName; // type conversion
if(string.Compare(n1,n2) > 0) { return 1; }
else if(string.Compare(n1,n2) < 0) { return -1; }
else { return 0; }
} // end method CompareNames()
public override string ToString()
{ return FirstName + " " + LastName; }
} // end class Name
class SimpleDelegate
{
Name[] names = new Name[4];
public SimpleDelegate()
{
names[0] = new Name("John","Smithlone");
names[1] = new Name("Carl","Xenon");
names[2] = new Name("Rolf","Cooper");
names[3] = new Name("Ola","Jones");
}
static void Main()
{
SimpleDelegate sd = new SimpleDelegate();
// the delegate instantiation
Comparer cmp = new Comparer(Name.CompareFirstNames);
Console.WriteLine("\nBefore Sort:\n");
sd.PrintNames();
// observe the delegate argument
sd.Sort(cmp);
82 CHAPTER 5. OBJECT-ORIENTED PROGRAMMING
Console.WriteLine("\nAfter Sort:\n");
sd. PrintNames();
}
public void Sort(Comparer compare)
{
object temp;
for(int i=0;i
{
for(int j=i;j
{
// using delegate "compare" just like a normal method
if(compare(names[i],names[j])>0)
{
temp = names[i];
names[i] = names[j];
names[j] = (Name) temp;
} // end if
}
}
} // end method Sort
public void PrintNames()
{
Console.WriteLine("Names:\n");
foreach(Name name in names)
{
Console.WriteLine(name.ToString());
}
} // end method PrintNames()
}
No comments:
Post a Comment