The X# Documentation Project

This forum is meant for anything you would like to share with other visitors
User avatar
Chris
Posts: 4936
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

The X# Documentation Project - new topics last week

Post by Chris »

Hi Karl,

You use delegates when you need to call some code that is not known at compile time. For example you are creating a library with a function that does very heavy calculations and takes a lot of time to complete. Because of that, from time to time you want your library to send back some data (to the app that uses it) about its progress and maybe let the calling app decide if it wants to cancel the operation (random example that comes to mind, a windows update that takes too long time to complete and makes your system crawl and you cannot work, so you are MS and want to give some reliable info back to the user about the progress and allow him to cancel the %^%&#@ update).

This can be nicely done with a delegate. You can define one as:

Code: Select all

DELEGATE LongProcessProgressReportDelegate(oInfo AS ProgressInfo) AS LOGIC
and then simply use (simplified code)

Code: Select all

CLASS ToolInLibrary
EXPORT oProgreesDel AS LongProcessProgressReportDelegate

METHOD HeavyCalculations() AS VOID
// ...
IF MuchTimePassedSoMustReportBack
  LOCAL somedata AS ProgressInfo
  // ...
  somedata:TimeRemaining := 17 years
  IF .not. SELF:oProgreesDel:Invoke(somedata)
    // cancel operation
  ELSE
ENDIF
Your library code knows nothing about the actual code that will be actually called at runtime, it only knows (and enforces) that the method that will be called at runtime must and will have those specific parameters and return type that you specified in the delegate.

The calling code, the one which uses your library, would then need to implement a method with that signature (you know what I mean :)):

Code: Select all

CLASS TheRealApp
  METHOD DoSomeWork() AS VOID
    LOCAL oTool AS ToolInLibrary
    // ....
    oTool:oProgreesDel := ProgressCallbackMethod
    oTool:Heavycalculation()
    // wait and wait and wait...
  RETURN

  METHOD ProgressCallbackMethod(oInfo AS ProgressInfo) AS LOGIC
    SELF:DisplayProgressInfoToUser(oInfo)
    IF UserIsCompletelyAngryWithDelay()
      RETURN FALSE
    ENDIF
  RETURN TRUE
END CLASS
This will work nicely at runtime, your library will be giving updates to the main app in a type safe and fast manner. Instead of invoking a delegate, you could possibly use just Send() to call a method in the calling code, we had that in VO for decades you will say. Yes, but this is not type safe, there is no parameter and return type cheeking at compile time, so you can accidentally easily use wrong params in the ProgressCallbackMethod() callback, which will throw an error at runtime only. With the delegate system, all is checked at compile time and the calls are guaranteed to be made correctly.

Another way to implement the above scenario, would have been to declare an interface IProgressReport with a method member named ProgressCallbackMethod() and require the calling code to implement this interface, but this is probably overkill, to request such a thing just for a single callback method.

Btw, .Net has brought this concept a step further, by introducing EVENTs, which are basically a warper around delegates, but their mechanism is the same and they require the exact same delegate type. In the sample above, you can replace the line

Code: Select all

EXPORT oProgreesDel AS LongProcessProgressReportDelegate
with

Code: Select all

EXPORT EVENT ProgreesDel AS LongProcessProgressReportDelegate
and it is the same thing more or less. So usually you are using delegates with events (like all those events in the Form class, which all use the "EventHandler" DELEGATE), but there are cases where you still need to use a delegate in its simple form, specified in a LOCAL or class var, for example when needing to provide a callback to a Win32 API function. A delegate instantiated and holded in a LOCAL also has the advantage that it can be passed around in your code just like any other variable and instantiated by various places in your code. But admittedly, there are very few cases where you would really need to do that.

Anyway, again this post got a lot larger than planned, hope it clarifies things about delegates a bit, instead of making them look even weirder :)

Chris
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
User avatar
wriedmann
Posts: 3765
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

The X# Documentation Project - new topics last week

Post by wriedmann »

Hi Chris, hi Karl,

I have now added a link to this message to the Delegase topic in the wiki. This IMHO the best option.

Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Post Reply