U2U Article: "Programming with delegates and events in .NET" Copyright © 2002 by U2U nv/sa, Belgium. All rights reserved.
Please address any questions or suggestions to U2U.

Programming with delegates and events in .NET

Author: Wim UYTTERSPROT, wim@u2u.be

Part 1: Introducing Delegates
Part 2: Defining your own Delegate class

Part 3: Delegates Example: The Programmable Robot
Part 4: Introducing .NET Events
Part 5: .NET Events Example: The Programmable Robot(2)

Part 3: Delegates Example: The Programmable Robot

Introduction

It is trivial that a programmable robot is a robot that you can program. However, what does it mean? It means that the controller of the robot must be able to send at runtime a set of instructions to the robot object; the robot must than be able to execute those instructions. The difficulty hereby is that the set of instructions are unknown to the robot at design time. But as you may guess yet, the .NET delegates will help us out.
In order to keep it simple, we will begin with a robot class having limited implementation code. We just allow our robot objects to “step”.

The initial code

At creation, our robot receives a name and a starting position. The robot’s position cannot be modified directly; it can only change by sending a Step message to the robot.

public class Robot
{
  private string name;
  private Point location;

  public Robot(string name,
               Point location)
  {
    this.name = name;
    this.location = location;
  }

  public override String ToString()
  {
    return "Robot " + name
    + " at " + location;
  }

  public void Step(Size size)
  {
    location += size;
    Console.WriteLine(ToString());
  }
}
Public Class Robot
  Private name As String
  Private location As Point

  Public Sub New(name As String, _
                 ByVal location As Point)
    Me.name = name
    Me.location = location
  End Sub

  Public Overrides Function ToString()_
    As String
    Return "Robot " & name & _
           " at " & location.ToString()
  End Function

  Public Sub [Step](ByVal size As Size)
    location.Offset(size.Width, 
    size.Height)
    Console.WriteLine(ToString())
  End Sub

End Class

The Step method represents the basic movement of the robot. Now let us add a robot control center; for the moment, it will only tests the good working of the communication with the robot. Later on, the control center will use the Step method in the instructions sent to the robots at runtime:

public class ControlCenter
{
 
  public static void Main()
  {
    Point startpoint = new Point();
    Size stepsize = new Size(1,1);
 
    Robot r2d2 = new Robot("r2d2",
                       startpoint );
    for (int i=0; i<=10; i++)
    {
      r2d2.Step( stepsize );
    }
  }
}
Public Class ControlCenter
 
  Public Shared Sub Main()
    Dim startpoint As Point
    Dim stepsize As New Size(1, 1)
 
    Dim r2d2 As New Robot("r2d2", _
                          startpoint)
    Dim i As Integer
    For i = 0 To 10
        r2d2.Step(stepsize)
    Next
  End Sub
 
End Class

The instruction library

The robot control center will send instruction sets to the robots. Therefore, we define a special class InstructionLib, a collection of static or shared procedures containing the instructions to be sent to the robots. As example, we add three functions and four properties to it:

C#

public class InstructionLib
{
    public static Size StepLeft
    {
        get { return new Size(-1,0); }
    }
    public static Size StepRight
    {
        get { return new Size(+1,0); }
    }
    public static Size StepForward
    {
        get { return new Size(0,+1); }
    }
    public static Size StepBack
    {
        get { return new Size(0,+1); }
    }
 
    public static bool Swing(Robot robot, int delay)
    {
        Trace.WriteLine("Swinging");
        for (int i=0; i<=5; i++)
        {
            robot.Step(StepLeft);
            robot.Step(StepRight);
            robot.Step(StepForward);
            Thread.Sleep(delay);
        }
        return true;
    }
 
    public static bool ZigZag(Robot robot, int delay)
    {
        Trace.WriteLine("ZigZagging");
        for (int i=0; i<=5; i++)
        {
            robot.Step(StepLeft);
            robot.Step(StepRight);
            Thread.Sleep(delay);
        }
        return true;
    }
 
    public static bool Bump(Robot robot, int delay)
    {
        Trace.WriteLine("Bumping");
        for (int i=0; i<=2; i++)
        {
            robot.Step(StepBack);
            Thread.Sleep(delay);
        }
        return true;
    }
 
}

VB.NET

Public Class InstructionLib
    Public Shared ReadOnly Property StepLeft() As Size
        Get
            Return New Size(-1, 0)
        End Get
    End Property
    Public Shared ReadOnly Property StepRight() As Size
        Get
            Return New Size(+1, 0)
        End Get
    End Property
    Public Shared ReadOnly Property StepForward() As Size
        Get
            Return New Size(0, 1)
        End Get
    End Property
    Public Shared ReadOnly Property StepBack() As Size
        Get
            Return New Size(0, -1)
        End Get
    End Property
 
    Public Shared Function Swing(ByVal robot As Robot, ByVal delay As Integer) _
                                 As Boolean
       Trace.WriteLine("Swinging")
      Dim i As Integer
      For i = 0 To 5
        robot.Step(StepLeft)
        robot.Step(StepRight)
        robot.Step(StepForward)
        Thread.Sleep(delay)
      Next
    End Function
 
    Public Shared Function ZigZag(ByVal robot As Robot, ByVal delay As Integer) _
                                 As Boolean
      Trace.WriteLine("ZigZagging")
      Dim i As Integer
      For i = 0 To 5
        robot.Step(StepLeft)
        robot.Step(StepRight)
        Thread.Sleep(delay)
      Next
    End Function
 
    Public Shared Function Bump(ByVal robot As Robot, ByVal delay As Integer) _
                                 As Boolean
      Trace.WriteLine("Bumping");
      Dim i As Integer
      For i = 0 To 2
        robot.Step(StepBack)
      Next
    End Function
 
End Class

 

The above code allows us to adapt the Main function of the control center:

public class ControlCenter
{
  public static void Main()
  {
    Point startpoint = new Point();
    Size stepsize = new Size(1,1);
 
    Robot r2d2 = new Robot("r2d2",
                      startpoint );
    InstructionLib.Swing(r2d2, 10);
  }
}
Public Class ControlCenter

  Public Shared Sub Main()
    Dim startpoint As Point
    Dim stepsize As New Size(1, 1)
 
    Dim r2d2 As New Robot("r2d2",
                       startpoint)
    InstructionLib.Swing(r2d2, 10)
  End Sub

End Class

Here, the control center calls here the InstructionLib.Swing function with as argument the robot r2d2 and a delay of 10 milliseconds. Logically, the control center invokes each "Step" on the robot; the knowledge how to swing is not transferred as a whole to the robot but stays in the control center.

The Instruction delegate class

What we want to realize now is that the control center can define instructions in a library and transfer a set of instructions as a whole to the robot. Only then, we will obtain a programmable robot. This brings us to the concept of the delegate interpreted as a program that the control center can send to its robots.

We define our delegate type with a signature identical to the Swing, ZigZag and similar InstructionLib functions:

//C#

public delegate bool Instruction( Robot robot, int delay );

' VB.NET

Public Delegate Function Instruction(ByVal robot As Robot, _
                                     ByVal delay As Integer) As Boolean

Note that we choose to define our delegate outside the Robot class or any other class in order to make it accessible everywhere. However, you can do this differently.

The Instruction delegate reference

We will now adapt the robot’s code so that it becomes a programmable robot. Therefore, our robot must be able to do two things: firstly, it must be able to accept a program, i.e. an Instruction object. We realize this by defining a public instance property together with an instance variable of type Instruction. In C# the property is named Program, the variable program; in VB.NET respectively Program and programValue. Secondly we define a StartProgram instance method that will invoke the Instruction delegate object:

C#

public class Robot
{
    private string name;
    private Point location;
    private Instruction program;
 
    public Robot(string name, Point location)
    {
        this.name = name;
        this.location = location;
    }
    public override String ToString()
    {
        return "Robot " + name + " at " + location;
    }
    public Instruction Program
    {
        get {return program; }
        set {program = value; }
    }
    public void StartProgram()
    {
        program(this, 10);
    }
    public void Step(Size size)
    {
        location += size;
        Console.WriteLine(ToString());
    }
}

VB.NET

Public Class Robot
    Private name As String
    Private location As Point
    Private programValue As Instruction
 
    Public Sub New(ByVal name As String, ByVal location As Point)
        Me.name = name
        Me.location = location
    End Sub
 
    Public Overrides Function ToString() As String
        Return "Robot " & name & " at " & location.ToString()
    End Function
 
    Public Property Program() As Instruction
        Get
            Return programValue
        End Get
        Set(ByVal Value As Instruction)
            programValue = Value
        End Set
    End Property
 
    Public Sub StartProgram()
        programValue(Me, 10)
 
        ' alternative invokes
        programValue.Invoke(Me, 10)
        Program()(Me, 10)
        Program.Invoke(Me, 10)
    End Sub
 
    Public Sub [Step](ByVal size As Size)
        location.Offset(size.Width, size.Height)
        Console.WriteLine(ToString())
    End Sub
End Class

We remark that the implementation code of the StartProgram method invokes the delegate field program directly. It is also possible to invoke the delegate property Program instead.

C#

The code to invoke delegate properties in C# syntax stays simple:

    public void StartProgram()
    {
        Program(this, 10);          // using the delegate property
    }

VB.NET

In VB.NET it may become a little bit tricky, as we have to use the Invoke method:

    Public Sub StartProgram()
        Program.Invoke(Me, 10)     ' using the delegate property
    End Sub

Alternative ways to invoke the delegate in VB.NET are:

        programValue.Invoke(Me, 10)
        Program()(Me, 10)

The Instruction delegate objects

We are now ready to program and our robot. In the ControlCenter.Main function, we first create our program: therefore we create a delegate object of type Instruction and refer to it with program. The robot’s program is then uploaded by using a simple assignment. Finally, we send a StartProgram request to the robot, in order to ask the robot to execute the program. This proves that we obtained a programmable robot:

C#

public class ControlCenter
{
 
    static void Main(string[] args)
    {
        Trace.Listeners.Add(new WriterTraceListener(System.Console.Out));
        Point startpoint = new Point();
        Size stepsize = new Size(1,1);
        Robot r2d2 = new Robot("r2d2", startpoint );
 
        // (1) create the program
        Instruction program = new Instruction(InstructionLib.Swing);
 
        // (2) upload the program
        r2d2.Program = program;
 
        // (3) request to start the program
        r2d2.StartProgram();
    }
}

VB.NET

Public Class ControlCenter
 
    Public Shared Sub Main()
        Trace.Listeners.Add(New TextWriterTraceListener(System.Console.Out))
        Dim startpoint As Point
        Dim stepsize As New Size(1, 1)
        Dim r2d2 As New Robot("r2d2", startpoint)
 
        ' (1) create the program
        Dim program As New Instruction(AddressOf InstructionLib.Swing)
 
        ' (2) upload the program
        r2d2.Program = program
 
        ' (3) request to start the program
        r2d2.StartProgram()
    End Sub
 
End Class

Combining the Instruction delegate objects

Let us complicate things now and study the technique to upload several programs to the robot. We may realize this in different ways. Here we will combine several instructions first into one single program that we upload as a whole. But as an exercise, you may do it differently by creating several programs, and upload the one by one.

C#

public class ControlCenter
{
 
    static void Main(string[] args)
    {
        Trace.Listeners.Add(new WriterTraceListener(System.Console.Out));
        Point startpoint = new Point();
        Size stepsize = new Size(1,1);
        Robot r2d2 = new Robot("r2d2", startpoint );
 
        Instruction swing = new Instruction(InstructionLib.Swing);
        Instruction zigzag = new ruction(InstructionLib.ZigZag);
        Instruction bump = new Instruction(InstructionLib.Bump);
 
        // technique 1
        Instruction program = null;
        program += zigzag;
        program += bump;
        program += swing;
        program += bump;
 
        r2d2.Program = program;
        r2d2.StartProgram();
    }
}

VB.NET

Public Class ControlCenter
 
    Public Shared Sub Main()
        Trace.Listeners.Add(New TextWriterTraceListener(System.Console.Out))
        Dim startpoint As Point
        Dim stepsize As New Size(1, 1)
        Dim r2d2 As New Robot("r2d2", startpoint)
 
        Dim swing As New Instruction(AddressOf InstructionLib.Swing)
        Dim zigzag As New Instruction(AddressOf InstructionLib.ZigZag)
        Dim bump As New Instruction(AddressOf InstructionLib.Bump)
 
        Dim program As Instruction
 
        ' technique 1
        program = zigzag
        program = DirectCast(Instruction.Combine(program, bump), Instruction)
        program = DirectCast(Instruction.Combine(program, swing), Instruction)
        program = DirectCast(Instruction.Combine(program, bump), Instruction)
 
        r2d2.Program = program
        r2d2.StartProgram()
 
    End Sub
 
End Class

Another technique is to combine the instruction objects by means of the static Combine method:

C#

      // technique 2
      Instruction[] instructions = new Instruction[] {zigzag, , swing, bump};
      program = (Instruction) Instruction.Combine(instructions);

VB.NET

      ' technique 2
      Dim instructions As Instruction() = {zigzag, bump, swing, bump}
      program = DirectCast(Instruction.Combine(instructions), Instruction)

Output

The result of our testing code will be something like

Bumping
Robot r2d2 at {X=0,Y=1}
Robot r2d2 at {X=0,Y=2}
Robot r2d2 at {X=0,Y=3}
Swinging
Robot r2d2 at {X=-1,Y=3}
...
ZigZagging
Robot r2d2 at {X=-1,Y=12}
...
Bumping
...
Robot r2d2 at {X=0,Y=15}

See also:

Part 1: Introducing Delegates

Part 2: Defining your own Delegate class

Part 3: Delegates Example: The Programmable Robot

Part 4: Introducing .NET Events

Part 5: .NET Events Example: The Programmable Robot(2)


Read more next week:

Part 6: The Scribble Application

Part 7: Visual Inheritance with Windows Forms

Contact me Contact


Contact me Receive U2U Newsletter.
Looking for a challenging job Download Brochure On Site Training Looking for a challenging job
Favorites Favorites

Copyright © 1999-2010 by U2U