Callback functions fulfill a similar purpose to delegates in C#.
For example, the Win32 API provides a timing service, that is accessed by calling SetTimer. SetTimer is exported by a system DLL, but the mechanism is exactly the same as if used in a user dll. In your code you would access the timer by doing something like this:
void
CALLBACK
MyTimerCallback(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
// do something
}
...
TIMERPROC fn = &MyTimerCallback;
int delay = 500;
SetTimer(NULL,0,delay,fn);
Calling SetTimer, and passing in the callback function, allows the operating system to call back into the function each time the timer ticks. Of course, there is no multicast capability here, and, especially in the case of SetTimer, the callback function must be a C function or static class method. There is no class or object instance associated with the function.
A similar pattern could be done in .NET - Im sure .NET has its own Timer paradigm but for a moment we could pretend that it implements a SetTimer function that takes a TimerDelegate.
In user code, in an object you would then define the MyTimerProc as a function with a signature that matches the delegate. And invoke it like this
TimerDelegate d = new TimerDelegate(myObject.MyTimerProc);
SetTimer(0,0,delay,d);
Or, if "timers" was an event that matched the TimerDelegate, then the equivalent C# code would look something like:
timers += new TimerDelegate(myObject.MyTimerProc);
Note: My C# is very rusty so don't take those code samples as any kind of example of either best practices, or even working code :P
When defining your own callback functions it is good practice to always define callback functions to take a void* "context" parameter, as that allows C++ programmers to store their "this" pointer and retrieve it.
// the first parameter to the callback fn is a void* user supplied context parameter
typedef void (CALLBACK* MyCallbackFn)(void* context, int etc);
// and, the dll export function always takes a function pointer, context parmeter pair.
DLL_EXPORT void SomeExportedFn(MyCallbackFn, void* context);