Delegates
Updated 531 Days AgoPublic

Delegates allows you to hold a function pointer in a variable, and use them at a later time.

The following three classes will work together to demonstrate creating, defining, and calling delegates.

{P589}
The DelegateHolder class contains a delegate member variable Greeting that is set in the constructor to the member function Introduce().

{P588}
The MyMathLib class contains just a single static function that returns the product of exponentiation. This function will be used to demonstrate how to use a delegate in the next and final class.

{P590, lines=110}

The DelegateDriver class covers a number of different ways to define and call a delegate. Lines 10 and 11 create two DelegateHolder objects, gh and ef, while line 14 creates a delegate variable that's set to the delegate variable found in object gh:

Drive Lines 10 - 14
var gh = new DelegateHolder("gh");
var ef = new DelegateHolder("ef");

var getName : delegate() = gh.Greeting;

Line 17 demonstrates directly calling a delegate using a delegate variable, while line 20 reassigns that delegate to the delegate variable found in object ef:

Driver Lines 17 & 20
getName();    

getName = ef.Greeting;

Line 24 passes the delegate variable into a function, DelegateCaller, which is defined further down the script—that calls the delegate function of any delegate passed into it:

Driver Line 24
getName = DelegateDriver.DelegateTest;

Line 27 assigns a static function to the delegate variable, DelegateTest, which defines its own delegate function assigned the to Exponent function in MyMathLib:

Driver Line 27
getName = DelegateDriver.DelegateTest;

Line 30 then passes in the re-assigned variable back into the DelegateCaller function, showing that the Exponent function is still called:

Driver Line 30
DelegateDriver.DelegateCaller(getName);

Line 33 creates a new delegate variable assigning it to another static function, while lines 35 - 44 demonstrates passing a delegate (that is redefined a couple of times) into a function that expects a delegate as a parameter:

Driver Lines 33-44
var doMath : delegate(a: Integer, b: Integer): Integer = DelegateDriver.Add;
    
Console.WriteLine(DelegateDriver.BinaryIntOpsCaller(doMath)); // 8
Console.WriteLine(doMath(2,6));                       // 8

doMath = DelegateDriver.Multiply;
Console.WriteLine(DelegateDriver.BinaryIntOpsCaller(doMath)); // 15
Console.WriteLine(doMath(2,6));                       // 12

doMath = DelegateDriver.Subtract;
Console.WriteLine(DelegateDriver.BinaryIntOpsCaller(doMath)); // 2
Console.WriteLine(doMath(2,6));

Attaching DelegateDriver to an object and running the project results in the following print statements in the Console Window:

Console Output
---------------- Begin Game ---------------
Hi I'm gh
Hi I'm ef
Hi I'm ef
256
256
8
8
15
12
2
-4

Named Parameters

IMPORTANT: At this time the names chosen for the parameters are part of the signature. In order for two function signatures to be the same the names chosen for the parameters must match:
MyMathLib
class MyMathLib
{
  [Static] // Note named parameters are lhs and rhs:
  function Exponent(lhs: Integer, rhs: Integer): Integer
  {
    return lhs^rhs;
  }
}

Consider these tests:

Invalid Delegate Parameter Names
class Driver
{
  function DelegateTest()
  {
    // Here the delegate has named parameters a and b:
    var doMath : delegate(a: Integer, b: Integer): Integer = MyMathLib.Exponent;// This won't compile.
  }
}
Console Output
The value being assigned to 'doMath' must be of type 'delegate (a : Integer, b : Integer) : Integer'. 
Its type is 'delegate (lhs : Integer, rhs : Integer) : Integer'.
Excluded Parameter Names
class Driver
{
  function DelegateTest()
  {
    // When declaring a delegate you cannot exclude the parameter names:
    var doMath : delegate(Integer, Integer): Integer = MyMathLib.Exponent;// This won't compile.
  }
}
Console Output
Function declaration 'delegate' has an invalid argument list. We found 'UpperIdentifier' but 
we expected to find ')'.
Correctly Named Parameters
class Driver
{
  function DelegateTest()
  {
    // Here, after making the parameter names match, does it work:
    var doMath : delegate(lhs: Integer, rhs: Integer): Integer = MyMathLib.Exponent;
    Console.WriteLine(doMath(2,8));
  }
}
Console Output
---------------- Begin Game ---------------
256

Member Function Delegates

IMPORTANT: Delegates that point at member functions can create memory leaks if they make cycles. See Memory Management for more.
IMPORTANT: Delegates pointing to member functions will throw runtime errors if the instance it was bound with was destroyed:
Invalid Call to a Delegate from a Deleted Variable
// Here are three Objects with the ability to hold delegates:
var ab = new DelegateHolder("ab");
var b = new DelegateHolder("b");

ab.Greeting = b.Introduce;

delete b;
ab.Greeting(); // This throws a runtime error
Console Output
Attempted to access a member of a null handle: Attempted to call a member function on a null object

Null Delegates

IMPORTANT: It is an illegal operation to call a null delegate, so be sure to initialize your delegates before calling!
// Here are three Objects with the ability to hold delegates:
var ab: delegate(a: Integer, b: Integer): Integer  = null;

ab(3, 6);// This throws a runtime error
Console Output
Attempted to invoke a null delegate

Related Materials

Manual

Memory Management
Classes and Structs
Attributes

Last Author
arend.danielek
Last Edited
Aug 8 2017, 12:42 PM
Projects
Subscribers
None