I’ve come across a number of write-ups some of which are conflicting each other when it comes to callbacks and delegates…  So, I decided to create this post, which is my understanding of callbacks and delegates as applicable to .NET developers.

 

First, the callbacks… 

 

A callback is a function that will be called when some asynchronous (i.e. not-blocking) operation determines that it’s the right time…  I’m purposely not saying that it will be invoked when another operation completes (as some do), because it’s only one of the conditions… 

Callbacks have been at the root of Windows programming for years.  Remember the old WndProc – an application defined (i.e. custom) function that was called when a message (e.g. key press, mouse down, show window) was sent to a window?

 

LRESULT CALLBACK WndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)

{

       switch (uMsg)

       {

              case WM_CREATE:

                     . . .

                     break;

              case WM_COMMAND:

                     . . .

                     break;

              case WM_DESTROY:

                     . . .

                     break;

default:

return DefWindowProc(hwnd,uMsg,wParam,lParam);

break;

       }

}

WndProc is a callback function because it’s called by the Windows operating system when an agreed upon condition is met (in this case, a Windows message is sent to a window that belongs to the application).

 

There are numerous other examples of callbacks – e.g. calling one of your functions when an asynchronous long running operation completes (e.g. some lengthy calculation or submitting data to database)…

 

Bottom line – a callback is a process of registering a function (method) so that it can be invoked at a later time and with varying arguments.  In C/C++, it would be referred to as a function pointer…

 

 

So, what about delegates?

In .NET, a delegate is a data structure (a class) that refers to either a static method or a class instance and an instance method of that class.  Some refer to it as a type-safe pointer…

 

In .NET framework, there is an abstract class called Delegate found in the System namespace.  Take a look at http://msdn2.microsoft.com/en-us/library/system.delegate_members.aspx.  As you can see, Delegate is a class that “represents” a method that can be called; in fact, it can call (a.k.a. invoke) that method (see DynamicInvoke method).   In fact, in .NET, callbacks and event listeners (e.g. button1_click) is implemented using delegates.

 

 

Delegates can point to more than one function; for example, as long as function declarations match, you can call multiple functions in one line.  For delegates that represent multiple methods, the .NET framework provides methods of the Delegate and MulticastDelegate classes that support operations such as adding a method to a delegate's invocation list.

 

Delegates are thread-safe = can hook up and unhook from delegates without worrying about synchronization.

 

Here is one example of a delegate:

 

public delegate bool IsGreater(int v1, int v2);

 

// NOTE: this function must match the signature

//       (i.e. the number and types of arguments and

//        return type of the delegate declared above)

public bool MyIsGreater(int x1, int x2)

{

       // Your implementation here

       return true;

}

 

. . .

 

// Tell the sort method to use MyIsGreater implementation

DoSort(mydata, new IsGreater(MyIsGreater));

 

. . .

 

// Generic sorting routine that doesn’t distinguish

// between different sorting algorithms

public void DoSort(int[] data, IsGreater GreaterThan)

{

       if (GreaterThan(data[i], data[j]))

       {

               // . . .

       }

}

 

Here is another example of a delegate:

 

// Define the event signature

public delegate void EventHandler(object sender, EventArgs e);

 

 

// Define the event and firing logic

public class Button

{ 

       // Define the event using the delegate signature

       public event EventHandler Click;

      

       protected void OnClick(EventArgs e)

       {

              // By calling Click(this, e), all classes that subscribed to receive the event, will receive it (one call => multiple receivers)

              if (Click != null)

                     Click(this, e);

       }

}     

 

 

. . .

 

// Define and register event handler

public class MyForm: Form

{

   Button okButton;

 

   public MyForm() {

      okButton = new Button(...);

      okButton.Caption = "OK";

      okButton.Click += new EventHandler(OkButtonClick);

   }

 

   void OkButtonClick(object sender, EventArgs e) {

      ShowMessage("You pressed the OK button");

   }

}

 

 

Here is one more example, just for good measure…

 

Client application:

 

private ChatServer cs = new ChatServer();

 

// Callback

private void CallMe(String msg)

{

. . .

}

 

// Let the server know we want to know when a message arrives

cs.ClientConnect(new ChatServer.OnMsgArrived(CallMe));

 

ChatServer application:

 

// Delegate (defines the callback signature)

public delegate void OnMsgArrived(String message);    

 

// List of methods to call

private static OnMsgArrived _methodsToCall;

 

// A way for clients to “register” their callbacks

public void ClientConnect(OnMsgArrived methodToCall)

{

       _methodsToCall = (OnMsgArrived) Delegate.Combine(_methodsToCall, methodToCall);

}

 

// A way for clients to “unregister” their callbacks

public void ClientDisconnect(OnMsgArrived methodToCall)

{

       _methodsToCall = (OnMsgArrived) Delegate.Remove(_methodsToCall, methodToCall);

}

 

// An internal function that’s called when a message arrives

public void MessageArrived(String msg)  

{

       // Let the registered clients know the message arrived

       Delegate[] DelegateList = _methodsToCall.GetInvocationList();

 

       for (int i = 0; i < DelegateList.Length; i++)

              ((OnMsgArrived) DelegateList[i])(msg);

}

 

       

           

Summary:  I hope this post demonstrated that delegates are a type-safe implementation of callbacks in .NET that is not limited to a single function pointer.