Interfaces, Callbacks, Delegates and Events

42
Object Oriented Programming Interfaces, Callbacks Interfaces, Callbacks Delegates and Events Delegates and Events

description

C# Programing

Transcript of Interfaces, Callbacks, Delegates and Events

  • Object Oriented Programming

    Interfaces, Callbacks Delegates and Events

  • Contents

    Introduction to interfacesExample An IMeasureable interfaceCallback functionsDelegatesEvents and event handlersSummary

  • Introduction to interfaces

    We have already seen how an abstract base class links related derived class through inheritance The overridden virtual methods of the abstract base class must be implemented in the derived classesThere are also occasions when we want unrelated classes to exhibit similar behaviourFor example we may want to be able to implement a sorting algorithm on unrelated classes
  • Introduction to interfaces

    For example, we may want to sort objects of class Square on the basis of the length of their sideWe could easily do this through polymorphism by implementing an abstract base class Sortable and using similar generic programming techniques we looked at in the last lectureBut, C# (along with Java) doesnt support multiple inheritance
  • Introduction to interfaces

    Shape

    Square

    Sortable

  • Introduction to interfaces

    An interface is simply a list of public methods that any class which implements that interface must provide implementations ofUnrelated classes can implement a single interfaceIn our example, we can define a ISortable interface such that objects of any class which implements this interface can be sorted on the basis of some comparative measureOur Square class can implement the ISortable interface using the length of side as the comparative measure
  • Introduction to interfaces

    public interface ISortable

    {

    int compareTo(ISortable s);

    }

    Our ISortable interface contains a single public method compareTo() which defines how the comparison is made This method is overridden in classes which implement this interface
  • Introduction to interfaces

    compareTo(ISortable b) returns 1,0 or 1 depending on whether some chosen instance field f of a ISortable object is such that :this.fb.fWe can implement this method in class Square to sort on the basis of length of side
  • Introduction to interfaces

    public class Square : Shape, ISortable

    {

    private int side;

    public Square(int s) { side = s; }

    public override void draw() { }

    public override double area() { return side * side; }

    public int compareTo(ISortable s)

    {

    Square sq=(Square)s;

    if (side sq.side) return 1;

    return 0;

    }

    }

  • Introduction to interfaces

    We can provide a ShellSort() method which implements the shell sort algorithm on arrays on ISortable objectsThe compareTo() method of the objects implementing the interface is called inside ShellSort()
  • Introduction to interfaces

    public class ArrayAlg

    {

    public static void shellSort(ISortable[] a)

    {

    int n = a.Length;

    int incr = n / 2;

    while (incr >= 1)

    {

    for (int i = incr; i < n; i++)

    {

    ISortable temp = a[i];

    int j = i;

    while (j >= incr && temp.compareTo(a[j - incr]) < 0)

    {

    a[j] = a[j - incr];

    j -= incr;

    }

    a[j] = temp;

    }

    incr /= 2;

    }

    }

    }

  • Introduction to interfaces

    To test our program we simply create an array of Square objects and pass it into the ShellSort() methodBecause of the cast in the compareTo() method in Square, the correctly implemented compareTo() method is called through polymorphism
  • Introduction to interfaces

    public class SortablesTest

    {

    static void Main(string[] args)

    {

    Square[] s = new Square[3];

    s[0] = new Square(3);

    s[0] = new Square(2);

    s[0] = new Square(1);

    ArrayAlg.shellSort(s);// Sorts on length of side

    }

    }

  • An IMeasureable interface

    Suppose we have a DataSet class which computes simple statistics of numbers read from an input streamFor example, the average and maximum

    DataSet

    average

    maximum

    Input stream

  • public class DataSet

    {

    public DataSet()

    { sum = 0.0; maximum = 0.0; count = 0; }

    public void add(double x)

    {

    sum += x;

    if (count == 0 || maximum < x)

    maximum = x;

    count++;

    }

    public double getAverage()

    {

    if (count == 0) return 0;

    else return sum / count;

    }

    public double getMaximum()

    { return maximum; }

    private double sum, maximum;

    private int count;

    }

  • An IMeasureable interface

    Clearly we would have to modify the DataSet class if we wanted to get the average of a set of bank account balances or to find the coin with the highest value amongst a setDataSet is not re-useable as it standsHowever, if all classes that DataSet objects operate on implement an IMeasurable interface, then the class becomes more flexible
  • An IMeasureable interface

    Thus getMeasure() for BankAccount objects return the balance and for Coin objects returns the coin value

    public interface IMeasureable

    {

    double getMeasure();

    }

  • An IMeasureable interface

    The IMeasurable interface expresses the commonality amongst objectsThe fact that each measurable objects can return a value relating to its sizeDataSet objects can then be used to analyse collections of objects of any class implementing this interface with minor modifications to the code
  • public class DataSet

    {

    public DataSet() { sum = 0.0; count = 0; }

    public void add(IMeasureable x)

    {

    sum += x.getMeasure();

    if (count == 0 ||

    maximum.getMeasure() < x.getMeasure())

    maximum = x;

    count++;

    }

    public double getAverage()

    {

    if (count == 0) return 0;

    else return sum / count;

    }

    public double getMaximum()

    { return maximum.getMeasure(); }

    private IMeasureable maximum;

    private double sum;

    private int count;

    }

  • An IMeasureable interface

    class MeasureablesTest

    {

    static void Main(string[] args)

    {

    DataSet d=new DataSet();

    Coin c1=new Coin(10);

    Coin c2=new Coin(20);

    d.add(c1);

    d.add(c2);

    double maxCoin=d.getMaximum();

    System.Console.WriteLine("coin max= " + maxCoin);

    }

    }

  • Callback functions

    The DataSet class is useful as a re-usable class but is still limitedThe IMeasurable interface can only be implemented by user defined classesWe cant, for example, find the maximum of a set of Rectangle objects as Rectangle is a pre-defined classWe can only measure an object in one way. For example, in the case of BankAccount objects, we can only measure it in terms of the balance
  • Callback functions

    The solution is to delegate the measuring to a separate class rather than being the responsibility of the objects we are measuringWe can create a separate IMeasurer interface and implement a measure() method in objects implementing this interface

    public interface IMeasurer

    {

    double measure(Object anObject);

    }

  • public class DataSet

    {

    public DataSet(IMeasurer m)

    {

    measurer=m;

    }

    public void add(Object x)

    {

    sum=sum+measurer.measure(x);

    if (count==0 || maximum

  • Callback functions

    A DataSet object makes a callback to the measure() method of an object implementing the IMeasurer interface when it needs to measure an object (such as checking a bank balance)This is in contrast to calling the getMeasure() method of an object implementing the IMeasurable interfaceWe are now free to design any kind of measures on an object of any classFor example, we can measure Square objects by areaWe require a SquareMeasurer class which implements the IMeasurer interface
  • Callback functions

    public class Square : Shape

    {

    private int side;

    public Square(int s) { side = s; }

    public override void draw() { }

    public override double area() { return side * side; }

    }

    public class SquareMeasurer : IMeasurer

    {

    public double measure(Object anObject)

    {

    Square sq=(Square) anObject;

    return sq.area();

    }

    }

  • Callback functions

    class MeasurersTest

    {

    static void Main(string[] args)

    {

    IMeasurer m = new SquareMeasurer();

    DataSet data = new DataSet(m);

    // Add squares to the data set

    data.add(new Square(5));

    data.add(new Square (30));

    // Get maximum

    double max = data.getMaximum();

    }

    }

  • Callback functions

    The SquareMeasurer object in data carries out the callback to the measure() methodWe have flexibility over our implementation of SquareMeasurer so that any feature of Square objects can be measuredOr even defining several measurer classes to measure different featuresCallbacks are used extensively in building graphical user interfacesA callback function is added to an event object which is called when the event is triggeredA managed way of doing this is to encapsulate the callback function into a delegate object
  • Delegates

    A delegate object holds a reference to a method with a pre-defined signatureA signature is simply the argument list and return type of the methodThe keyword delegate specifies that we are defining a delegate objectFor example we can define a delegate object myDelegate which holds a method which returns void and takes an int and a double as arguments

    public delegate void myDelegate(int arg1, double arg2)

  • Delegates

    A delegate object is initialized with a (callback) method The method signature must match the delegate signature

    public delegate void myDelegate(int arg1, double arg2);

    public class App

    {

    public static void Main()

    {

    myDelegate call = new myDelegate(aMethod);

    }

    static void aMethod(int k, double x) {}

    }

  • Delegates

    Delegate objects can be initialized with several method calls using the += operatorThe method calls can then be invoked in a chain by passing the correct arguments to the delegate objectEssentially it amounts to calling methods through a proxy object and is a powerful mechanism for event handling as we shall see
  • Delegates

    myDelegate(int,double)

    call

    aMethod()

    anotherMethod()

    Invoked by

    call(1,2.0)

    aMethod(1,2.0)

    anotherMethod(1,2.0)

  • Delegates

    public delegate void myDelegate(int arg1, double arg2);

    public class App

    {

    public static void Main()

    {

    myDelegate call = new myDelegate(aMethod);

    call += new myDelegate(anotherMethod);

    call(1, 2.0);

    }

    static void aMethod(int k, double x)

    {

    System.Console.WriteLine("aMethod " + k + " " + x);

    }

    static void anotherMethod(int k, double x)

    {

    System.Console.WriteLine("anotherMethod " + k + " " + x);

    }

    }

  • Delegates

  • Events and event handlers

    C# has built in support for event handlingEvent handling is usually used in GUIs such as to notify an application when a mouse click or button press has occurredBut any type (not just graphical types) can use events to allow notification of any kindA type must register its interest in handling a specific event with an event handler with a pre-defined signatureThis is achieved using a delegate object
  • Events and event handlers

    A type can define an event and an corresponding event handler which is a delegate object

    public class MyType

    {

    public delegate void EventHandler(int arg1, int arg2);

    public event EventHandler myEvent;

    .

    .

    }

  • Events and event handlers

    An application can then register its interest in an event by adding an initialized delegate object to the event using the += operator

    public class App

    {

    public static void Main()

    {

    MyType m = new MyType();

    m.myEvent += new MyType.EventHandler(myHandler);

    }

    public static void myHandler(int i1, int i2)

    {

    // Event handler code

    }

    }

  • Events and event handlers

    Equivalent code is very common in GUI development where applications register their own event handlers to respond to events generated by graphical components such as button clicksNormally such code is automatically generated if we are using visual programming techniques (drag and drop)However, it is still important to understand how it all works
  • Events and event handlers

    For example the following code snippet registers a graphical applications event handler to respond to button clicks

    public class MyGraphicalApp

    {

    Button button = new Button();

    button.Click += new EventHandler(HandleButtonClick);

    public void HandleButtonClick(Object sender EventArgs e)

    {

    // Event handler code

    }

    }

    public class Button

    {

    public delegate void EventHandler(.....);

    public event EventHandler Click;

    .

    .

    }

  • Events and event handlers

    For example, we can generate our own SafeArray class which fires an event on trying to access it beyond its boundsWe pass the array index into the event handlerIn our simple application, the event handler simply prints out an error messageIn an embedded system application, the array could be re-allocated to be a larger size in the event handler
  • Events and event handlers

    public class SafeArray

    {

    public delegate void OutOfBoundsEventHandler(int arg1);

    public event OutOfBoundsEventHandler myOutOfBoundsEvent;

    private int[] data;

    private int numberOfElements=0;

    public SafeArray(int n)

    {

    numberOfElements = n;

    data = new int[numberOfElements];

    }

    public int access(int elem)

    {

    if (elem < numberOfElements)

    return data[elem];

    else

    myOutOfBoundsEvent(elem); // Fire an event

    return 0;

    }

    }

  • Events and event handlers

    public class App

    {

    public static void Main()

    {

    SafeArray s = new SafeArray(10);

    s.myOutOfBoundsEvent += new SafeArray.OutOfBoundsEventHandler(myHandler);

    s.access(7);

    s.access(11);// Out of bounds event generated!

    }

    public static void myHandler(int i1)

    {

    System.Console.WriteLine("Index " + i1 + " out of bounds ");

    }

    }

  • Summary

    We have seen how objects of unrelated classes can exhibit similar behaviour by implementing an interfaceInterfaces support generic programming and avoid multiple inheritanceWe looked at a detailed example involving a IMeasurable interfaceWe also introduced callbacks by having a separate measurer object implemting an IMeasurer interfaceA callback function was made to a measure() method which returned some measure on the object passed to itWe have seen how delegates are objects which encapsulate method callsThese method calls can be chained using the += operatorWe have seen how we can create types which can generate events and how applications can register their interest in events by initializing a delegate object with their own event handler