IBM Support

How to model delegate objects in .NET edition

Question & Answer


Question

How do you forward engineer C# delegates and events with Rational XDE Professional v. 2002 Microsoft .NET edition?

Cause

You use call back functions.

Answer

The solution is organized as follows:



  • PART A: REFERENCES ON DELEGATES
    This part provides background on how the delegate model works


  • PART B: MODELING DELEGATES WITH XDE, AN EXAMPLE
    This part uses an example to explain how to model
    and forward engineer delegates and events in XDE


  • PART C: IMPLEMENTING THE CODE
    This part shows how to implement the code generated by XDE
    to build and run the example.



PART A: REFERENCES ON DELEGATES


A C# delegate is essentially an object-oriented version of a C++ function pointer.

More information on delegates is available from the following Microsoft sources:

---
An Introduction to Delegates



Delegates, Part 2



Implementation of Events with Delegates



(mscorlib.dll) System.Delegate



(mscorlib.dll) System.MulticastDelegate




PART B: MODELING DELEGATES WITH XDE, AN EXAMPLE



B.1. CREATING THE 'Account' CLASS


This excerise uses the familiar example of a bank account. You can model this class in XDE.
To start, open .NET and create a new C# Console Application. Then, right click on the project and Synchronize. This creates a code model in the Model Explorer.
In the Model Explorer, create an 'Account' class that has a private attribute called 'balance', of type 'int'.

This class also has two public operations called 'Withdraw' and 'Deposit', each taking an 'int' [in] parameter (the amount to be withdrawn/deposited) and returning void.

B.2. CREATING THE DELEGATE 'Account.AccountEventHandler'

The class Account should also have a nested class called AccountEventHandler, stereotyped as <<delegate>>.

To complete the delegate,add to AccountEventHandler only one operation called 'Invoke'.
The list of parameters of the Invoke operation expresses the signature of the callback function invoked when the event is fired.

It is custom to give the delegate the following arguments:
1. an object, representing the object that fired the event
2. a class inheriting from System.EventArgs to pass any needed parameters.

In this case, to fire events that communicate the actual balance, create a nested class Account.AccountEventArgs that inherits from System.EventArgs, and has one parameter of type 'int' called 'amount', one non-default constructor that takes this int parameter as an argument, and one getter that returns the 'amount'.

Now give the Account.AccountEventHandler.Invoke operation
1 [IN] parameter of type object,
1 [IN] parameter of type Account.AccountEventArgs,
1 [return] parameter of type void.

After Syncronizing the project, an inheritance relationship will be added in the model
from the delegate to the class System.Delegate even if the Intermediate Language subsequently produced by the compiler shows that the delegate inherits from System.MulticastDelegate instead. This is reported as defect RATLC00591955.

B.3. CREATING THE EVENTS 'Debit' and 'Credit'

Finally class Account will have two attributes (or associations) stereotyped as <<event>>, called 'Debit' and 'Credit', that should be fired whenever a change of the balance takes place, to signal if the updated balance is positive or negative.

If created as attributes, make their TypeExpression = Account.AccountEventHandler

If created as directed associations, draw the associations from
Account to AccountEventHandler.

To make these events static:

- for attributes: in the Property Editor, set their OwnerScope to 'Classifier' (rather than 'Instance')

- for association roles: in the property editor for the associations, locate which end
of the association has the name Debit (Credit). Then set End1OwnerScope or End2OwnerScope to 'classifier' (rather than 'Instance').

B.4. FORWARD ENGINEER THE CODE:

After creating the above class Account, right click on it in the Model Explorer
and choose 'Generate Code'.

Disclaimer

All source code and/or binaries attached to this document are referred to here as "the Program". IBM is not providing program services of any kind for the Program. IBM is providing the Program on an "AS IS" basis without warranty of any kind. IBM WILL NOT BE LIABLE FOR ANY ACTUAL, DIRECT, SPECIAL, INCIDENTAL, OR INDIRECT DAMAGES OR FOR ANY ECONOMIC CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN IF IBM, OR ITS RESELLER, HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.



The following code should be generated:



public class Account
{
public static event Account.AccountEventHandler Credit;
public static event Account.AccountEventHandler Debit;
private int balance;

public class AccountEventArgs : System.EventArgs
{
public AccountEventArgs(int arg0)
{
}
public int Get_Amount()
{
}
private int amount;
}

public delegate void AccountEventHandler(
object sender, AccountEventArgs e);

public void Withdraw(int amount)
{
}

public void Deposit(int amount)
{
}

}



PART C: IMPLEMENTING THE CODE:

Now implement the constructor of AccountEventArgs and the getter of amount:


public AccountEventArgs(int arg0)
{
amount = arg0;
}

public int Get_Amount()
{
return amount;
}



Then implement the Withdraw and Deposit methods so that they do the obvious calculations to update the balance, and so that they fire the 'Credit' event if the updated balance is positive, the 'Debit' event if the updated balance is negative:


public void Deposit(int amount)
{
System.Console.WriteLine("Called Deposit with amount: " + amount);
int CurrentBalance = balance + amount;

//CurrentBalance holds the updated balance while it is checked

if(CurrentBalance < 0)
{
//fire the event
Debit(this,new AccountEventArgs(CurrentBalance));
}
if (CurrentBalance >= 0)
{
//fire the event
Credit(this, new AccountEventArgs(CurrentBalance));
}
balance = CurrentBalance;

}

public void Withdraw(int amount)
{
System.Console.WriteLine("Called Deposit with amount: " + amount);
int CurrentBalance = balance - amount;

//CurrentBalance holds the updated balance while it is checked

if(CurrentBalance < 0)
{
//fire the event
Debit(this,new AccountEventArgs(CurrentBalance));
}
if (CurrentBalance >= 0)
{
//fire the event
Credit(this, new AccountEventArgs(CurrentBalance));
}
balance = CurrentBalance;

}



This completes the Account class. Its code in the end will be like:

------------------------------------------------------------------------------------------------------------------------------------



public class Account
{
public static event Account.AccountEventHandler Credit;
public static event Account.AccountEventHandler Debit;
private int balance;



public class AccountEventArgs : System.EventArgs
{
public int Get_Amount()
{
return amount;
}

private int amount;

public AccountEventArgs(int arg0)
{
amount=arg0;
}
}

public delegate void AccountEventHandler(object sender, AccountEventArgs e);


public void Deposit(int amount)
{
System.Console.WriteLine("Called Deposit with amount: " + amount);
int CurrentBalance = balance + amount;

//CurrentBalance holds the updated balance while it is checked

if(CurrentBalance < 0)
{
//fire the event
Debit(this,new AccountEventArgs(CurrentBalance));
}
if (CurrentBalance >= 0)
{
//fire the event
Credit(this, new AccountEventArgs(CurrentBalance));
}
balance = CurrentBalance;

}

public void Withdraw(int amount)
{
System.Console.WriteLine("Called Deposit with amount: " + amount);
int CurrentBalance = balance - amount;

//CurrentBalance holds the updated balance while it is checked

if(CurrentBalance < 0)
{
//fire the event
Debit(this,new AccountEventArgs(CurrentBalance));
}
if (CurrentBalance >= 0)
{
//fire the event
Credit(this, new AccountEventArgs(CurrentBalance));
}
balance = CurrentBalance;

}


}


------------------------------------------------------------------------------------------------------------------------------------
A driver application that creates an account , that calls the Deposit and Withdraw methods (triggering the events), and that reacts to the events is needed.


Create a class called DriverApplication, with a main function and two static methods that handle the events Credit and Debit: OnCredit and OnDebit.
These methods must have the same signature as the Invoke operation of the delegate.

Eventually the resulting code could be something like:
------------------------------------------------------------------------------------------------------------------------------------



using System;

class DriverApplication
{
//Reaction to the event 'Debit'
public static void OnDebit(object sender,Account.AccountEventArgs e)
{
Console.WriteLine("Event Debit: "+e.Get_Amount());
}

//Reaction to the event 'Credit'
public static void OnCredit(object sender,Account.AccountEventArgs e)
{
Console.WriteLine("Event Credit: "+e.Get_Amount());
}

static void Main(string[] args)
{

//Create an account
Account theAccount = new Account();
//Add to the Delegate a list of operations
//that have the same signature
//as the Invoke operation
Account.Credit += new
Account.AccountEventHandler(OnCredit);
Account.Debit += new
Account.AccountEventHandler(OnDebit);

// perform actions that will trigger events
theAccount.Deposit(100);
theAccount.Withdraw(250);

}
}


------------------------------------------------------------------------------------------------------------------------------------
After the account is built and run, the typical output will be:



C:\DelegatesEvents.exe
Called Deposit with amount: 100
Event Credit: 100
Called WithDraw with amount: 250
Event Debit: -150

[{"Product":{"code":"SSAVLQ","label":"Rational XDE Professional .NET Edition"},"Business Unit":{"code":"BU053","label":"Cloud \u0026 Data Platform"},"Component":"Modeling","Platform":[{"code":"PF033","label":"Windows"}],"Version":"2002.05.20.516.009","Edition":"","Line of Business":{"code":"","label":""}}]

Document Information

Modified date:
29 September 2018

UID

swg21127632