Вы находитесь на странице: 1из 36

// Write a program in C# to build a class which implements an interface which is already existing.

public class Point : ICloneable


{
public int x, y;
public Point() { }
public Point(int x, int y) { this.x = x; this.y = y; }
// Return a copy of the current object.
public object Clone()
{
//return new Point(this.x, this.y);
return this.MemberwiseClone();.
}
public override string ToString()
{
return string.Format("X = {0}; Y = {1}", x, y);
}
static void Main(string[] args)
{
// Notice Clone() returns a generic object type.
// You must perform an explicit cast to obtain the derived type.
Point p3 = new Point(100, 100);
Point p4 = (Point)p3.Clone();
// Change p4.x (which will not change p3.x).
p4.x = 0;
// Print each object.
Console.WriteLine(p3);
Console.WriteLine(p4);
GRNICA
}
}

// Demonstrating C# properties.
class SimpProp
{
int prop;
public SimpProp()
{
prop = 0;
}
public int MyProp
{
get
{
return prop;
}
set
{
if (value >= 0)
prop = value;
else
Console.WriteLine("U r trying to assign a -ve value...!");
}
}
}

class PropertyDemo
{
public static void Main()
{
SimpProp ob = new SimpProp();
Console.WriteLine("Original value of ob.MyProp: " + ob.MyProp);
ob.MyProp = 100; // assign value
Console.WriteLine("Value of ob.MyProp: " + ob.MyProp);
Console.WriteLine("Attempting to assign -10 to ob.MyProp");
ob.MyProp = -10;
Console.WriteLine("Value of ob.MyProp: " + ob.MyProp);
}
}

Callback Interfaces, Delegates,


and Events

Understanding Callback Interfaces


Interfaces define a behavior that may be supported by various types
in our system.
Beyond using interfaces to establish polymorphism, interfaces may
also be used as a callback mechanism.
This technique enables objects to engage in a two-way conversation
using a common set of members.
// The callback interface.
public interface IEngineEvents
{
void AboutToBlow(string msg);
void Exploded(string msg);
}

Event interfaces are not typically implemented directly by the


object, but rather by a helper object called a sink object
GRNICA

The sender of the events (the Car type in this case) will make calls on
the sink under the appropriate circumstances.
// Car event sink.
public class CarEventSink : IEngineEvents
{
private string name;
public CarEventSink() { }
public CarEventSink(string sinkName)
{
name = sinkName;
}
public void AboutToBlow(string msg)
{
Console.WriteLine("{0} reporting: {1}", name, msg);
}
public void Exploded(string msg)
{
Console.WriteLine("{0} reporting: {1}", name, msg);
}
GRNICA
}

Now we have a sink object that implements the event interface, our
next task is to pass a reference to this sink into the Car type.
The Car holds onto the reference and makes calls back on the sink.
In order to allow the Car to obtain a reference to the sink, we will
need to add a public helper member to the Car type that we will call
Advise().
Likewise, if the caller wishes to detach from the event source, it may
call another helper method on the Car type named Unadvise().
In order to allow the caller to register multiple event sinks, the Car
now maintains an ArrayList to represent each outstanding
connection:

GRNICA

class Car
{
int currSpeed, maxSpeed;
String name;
// The set of connected sinks.
ArrayList clientSinks = new ArrayList();
// Attach or disconnect from the source of events.
public void Advise(IEngineEvents sink)
{
clientSinks.Add(sink);
}
public void Unadvise(IEngineEvents sink)
{
clientSinks.Remove(sink);
}
public Car(String petName, int curSpeed, int maxspeed)
{
name = petName;
currSpeed = curSpeed;
maxSpeed = maxspeed;
}
GRNICA

To send the events, lets update the Car.Accelerate() method to


iterate over the list of connections maintained by the ArrayList and
fire the correct notification when appropriate.
class car
{
bool carIsDead;
public void Accelerate(int delta)
{
// If the car is 'dead', send Exploded event to each sink.
if (carIsDead)
{
foreach (IEngineEvents e in clientSinks)
e.Exploded("Sorry, this car is dead...");
}
else
{
currSpeed += delta;
// Send AboutToBlow event.
if (10 == maxSpeed - currSpeed)
{
foreach (IEngineEvents e in clientSinks)
e.AboutToBlow("Careful buddy! Gonna blow!");
}
if (currSpeed >= maxSpeed)
carIsDead = true;
else
Console.WriteLine("\tCurrSpeed = {0} ", currSpeed);
GRNICA
}
}

// Make a car and listen to the events.


public class CarApp
{
static void Main(string[] args)
{
Console.WriteLine("***** Interfaces as event enablers *****");
Car c1 = new Car("SlugBug", 100, 10);
// Make sink object.
CarEventSink sink = new CarEventSink();
// Pass the Car a reference to the sink.
c1.Advise(sink);
// Speed up (this will trigger the events).
for(int i = 0; i < 10; i++)
c1.Accelerate(20);
// Detach from event source.
c1.Unadvise(sink);
Console.ReadLine();
}
}

GRNICA

10

Understanding the .NET Delegate Type


The Windows API makes frequent use of C-style function pointers
to create entities termed callback functions or simply callbacks.
Using callbacks, programmers were able to configure one function
to report back to (call back) another function in the application.
Function pointers simply represent memory addresses and they do
not include information such as :
number of parameters
types of parameters
return type
calling convention

But in OOP methods rarely exist in isolation. They are usually


associated with a class instance before they can be called.
C# implements the callback technique in a much safer and more
object-oriented manner, using a GRNICA
kind of object called delegate object.
11

A delegate object is a special type of object that contains the details


of a method rather than data.
Defining a Delegate in C#

The dictionary meaning of delegate is a person acting for another


person.
In C# it means, a method acting for another method.
When we want to create a delegate in C#, we make use of the
delegate keyword.
The name of our delegate can be whatever we desire. However,
we must define the delegate to match the signature of the method it
will point to.

GRNICA

12

Creating and using delegates involve four steps. They are

Delegate Declaration
Delegate methods definition
Delegate Instantiation
Delegate Invocation
The delegate declaration defines a class using the class
System.Delegate as a base class.

GRNICA

13

Delegate Declaration
Modifier delegate return-type delegate-name (parameters);
Delegate is a keyword that signifies that the declaration represents a
class type derived from System.Delegate.

The return-type indicates the return of the delegate.


Parameters identifies the signature of the delegate.
Delegate-name is any valid C# identifier and is the name of the
delegate that will be used to instantiate delegate objects.
Modifiers control the accessibility of the delegate.
They include new, public, private, protected, and internal.
The new modifiers is permitted only on delegates declared within
another type.
GRNICA

14

Some examples of delegates are :

delegate void SimpleDelegate();


delegate int MathOperation(int x, int y);
public delegate string GetString();
public delegate int CompareItems(object o1, object o2);
delegate double DoubleOperation(double x);

The use of the keyword delegate tells the compiler that it is the
definition of a new class using the System.Delegate as the base
class.

GRNICA

15

Delegate Methods
The methods whose references are encapsulated into a delegate
instance are known as delegate methods or callable entities.
The signature and return type of the methods must exactly match the
signature and return type of the delegate.
For instance, the delegate
delegate string GetString()
can be made to refere to the method ToString() using an int as follows
int N = 100;
GetAString s1 = new GetAString(N.ToString);

GRNICA

16

The delegate
delegate void Delegate1();
can encapsulate references to the following methods :
public void F1()
{
Console.WriteLine("F1"); }
static public void F2()
{
Console.WriteLine("F2"); }

The delegate
delegate double MathOp(double x, double y);
can refere to any of the following methods:
public static double Multiply(double a, double b)
{
return (a*b);
}
public double Divide(double a, double b)
{
return (a/b);
}
GRNICA

17

delegate int ArithOp(int x, int y);


class MathOperation
{
public static int Add(int a, int b)
{
return (a + b);
}
public static int Sub(int a, int b)
{
return (a - b);
}
}
class DelegateTest
{
static void Main(string[] args)
{
ArithOp op1 = new ArithOp(MathOperation.Add);
ArithOp op2 = new ArithOp(MathOperation.Sub);
int result1 = op1(200, 100);
int result2 = op2(200, 100);
Console.WriteLine("Result1=" + result1);
Console.WriteLine("Result2=" + result2);
}
}

GRNICA

18

Multicast Delegates
So far the delegate has invoked only one method.
It is possible for certain delegates to hold and invoke multiple
methods. Such delegates are called as multiple delegaets
(combinable delegates).
But it must satisfy following conditions :
the return type of the delegate must be void.

none of the parameters of the delegate type can be declared as


output parameters, using out keyword.
If D is the delegate, d1,d2,d3,d4 are instance of D, then

d3 = d1 + d2; // refers to two methods


d4 = d3 d2; // d4 refers to only d1 method
GRNICA

19

delegate void MDelegate();


class DM
{
static public void Display()
{
Console.WriteLine("New Delhi");
}
static public void Print()
{
Console.WriteLine("New York");
}
}
class MTest
{
public static void Main()
{
MDelegate m1 = new MDelegate(DM.Display);
MDelegate m2 = new MDelegate(DM.Print);
MDelegate m3 = m1 + m2;
MDelegate m4 = m2 + m1;
MDelegate m5 = m3 - m2;
m3();
m4();
m5();
}

GRNICA

20

Understanding (and Using) Events


Delegates are fairly interesting constructs, they enable two
objects in memory to engage in a two-way conversation.
C# provides the ability for one object to call back to another object
is a helpful construct & C# provides the "event" keyword to lessen
the burden (defining, declaring, etc..) of using delegates in the raw.
The most use of the "event" keyword is found in GUI-based
applications, in which Button, TextBox, and Calendar widgets all
report back to the containing Form when a given action clicking
has occurred.
Establishing an event is a two-step process.

GRNICA

21

First, we need to define a delegate, which will be used to hold


the set of methods that will be called when the event occurs.
Next, we define the events themselves using the C# "event
keyword, which is defined in terms of the related delegate.

Events are declared using the simple event declaration format :


modifier event type event-name;

modifier is a valid combination of the four access modifier.


event is the keyword that signifies that the event-name is an event.
type of an event declaration must be a delegate type and the
delegate must be accessible as the event itself.
event-name is any valid C# variable name.
GRNICA

22

Examples of event declaration are :


public event EventHandler Click;
public event RateChange Rate;

EventHandler and RateChange are delegates and Click & Rate are
events.

GRNICA

23

Listening to Incoming Events


C# events also simplifies the registering of the caller-side event
handlers.
Rather than having to specify custom helper methods, the caller
simply makes use of the += and -= operators directly (which
triggers the correct add_XXX() or remove_XXX() method in the
background).
ObjectVariable.EventName += new AssociatedDelegate(functionToCall);

When you wish to detach from a source of events, use the -= operator:
ObjectVariable.EventName -= delegateObject;

GRNICA

24

public delegate void Edelegate(string str);


class EventClass
{
public event Edelegate Status;
public void TriggerEvent()
{
if (Status != null)
Status("Event Triggered");
}
}
class EventTest
{
public void EventCatch(string str)
{
Console.WriteLine(str);
}
public static void Main()
{

EventClass ec = new EventClass();


EventTest et = new EventTest();
ec.Status += new Edelegate(et.EventCatch);
ec.TriggerEvent();
}
GRNICA

25

Understanding Operator Overloading


C# has a canned set of tokens that are used to perform basic
operations on intrinsic types.
For example, we know that the + operator can be applied to two
integers in order to yield a larger integer:
// The + operator with ints.
int a = 100;
int b = 240;
int c = a + b; // c is now 340

The same + operator can be applied to most intrinsic C# data


types. For example, consider this code:
// + operator with strings.
string s1 = "Hello";
string s2 = " world!";
string s3 = s1 + s2; // s3 is now "HelloGRNICA
world!"

26

In essence, the + operator functions in unique ways based on the


supplied data types (strings or integers in this case).
When the + operator is applied to numerical types, the result is the
summation of the operands. When the + operator is applied to
string types, the result is string concatenation.

GRNICA

27

public class Point


{
private int x, y;
public Point(int xPos, int yPos)
{
x = xPos;
y = yPos;
}
public static Point operator +(Point p1, Point p2)
{
return new Point (p1.x + p2.x, p1.y + p2.y);
}
public static Point operator -(Point p1, Point p2)
{
return new Point(p1.x - p2.x, p1.y - p2.y);
}
public override string ToString()
{
return string.Format("[{0}, {1}]", this.x, this.y);
}
GRNICA

28

static void Main(string[] args)


{
Console.WriteLine("***** Fun with Overloaded Operators *****\n");
// Make two points.
Point ptOne = new Point(100, 100);
Point ptTwo = new Point(40, 40);
Console.WriteLine("ptOne = {0}", ptOne);
Console.WriteLine("ptTwo = {0}", ptTwo);
// Add the points to make a bigger point?
Console.WriteLine("ptOne + ptTwo: {0} ", ptOne + ptTwo);
// Subtract the points to make a smaller point?
Console.WriteLine("ptOne - ptTwo: {0} ", ptOne - ptTwo);
Console.ReadLine();
}
}
GRNICA

29

// overloading + operator
class Complex
{
double x, y;
public Complex()
{
}
public Complex(double real, double imag)
{
x = real;
y = imag;
}
public static Complex operator +(Complex c1, Complex c2)
{
Complex c3 = new Complex();
c3.x = c1.x + c2.x;
c3.y = c1.y + c2.y;
return (c3);
}
public void display()
{
Console.Write(x);
Console.WriteLine("+j" + y);
Console.WriteLine();
}
GRNICA
}

30

class ComplexTest
{
static void Main(string[] args)
{
Complex a= new Complex(2.5,3.5);
Complex b= new Complex(1.6,2.7);
Complex c = a + b;
Console.Write(" a = ");
a.display();
Console.Write(" b = ");
b.display();
Console.Write(" c = ");
c.display();
}
}

GRNICA

31

Building a Custom Indexer


Array indexing is performed using the [ ] operator.
It is possible to overload the [ ] operator for classes that we create,
but we dont use an operator method.

Instead, we create an indexer. An indexer allows an object to be


indexed like an array.
The main use of indexers is to support the creation of specialized
arrays that are subject to one or more constraints.
We can use an indexer for any purpose for which an array-like
syntax is beneficial. Indexers can have one or more dimensions.

General syntax :
element-type this[int index]
{
// The get accessor.
get {
// return the value specified by index
}
// The set accessor.
set {
// set the value specified by index
}

element-type is the base type of the indexer. Each element accessed


by the indexer will be of type element-type. The element type
corresponds to the base type of an array.

The parameter index receives the index of the element being


accessed. Here parameter need not have to be of type int, but since
indexers are typically used to provide array indexing, an integer type
is customary

public class SpellingList


{
protected string[] words = new string[size];
static public int size = 10;
public SpellingList()
{
for (int x = 0; x < size; x++ )
words[x] = String.Format("Word{0}", x);
}

public string this[int index]


{
get
{
string tmp;

if( index >= 0 && index <= size-1 )


tmp = words[index];
else
tmp = "";
return ( tmp );
}
set
{
if( index >= 0 && index <= size-1 )
words[index] = value;
}
}
}

public class Indexer


{
public static void Main()
{
SpellingList myList = new SpellingList();
myList[3] = "=====";
myList[4] = "Brad";
myList[5] = "was";
myList[6] = "Here!";
myList[7] = "=====";
for ( int x = 0; x < SpellingList.size; x++ )
Console.WriteLine(myList[x]);
}
}

Вам также может понравиться