Submitted by BillB on 10/22/2010
(If you have a minute, please add a comment when you're done so I know if I'm presenting this well enough.)
C# Callbacks with Interfaces and Delegates
10/6/2009Contents
- Callbacks
- Why Callbacks
- C# Callback with an Interface
- C# Callback with a Delegate
- When to Use Delegates Instead of Interfaces
- An Example You Can Experiment With
- Real World Examples in .Net : Sorting, Events, LINQ
This article gives an example of a callback implemented two ways: with a delegate and with an interface. I cover the relevant basics of interfaces and delegates for those new to them as well as a quick overview of callbacks in software design. Delegates in the form of anonymous methods and lambdas are also shown. The concept of a callback may be unfamiliar to anyone new to .Net so I hope I've given adequate background.
Callbacks 
I'm going to start out real basic.
Code libraries like .Net provide an API, (Application Programming Interface), which is basically a bunch of exposed methods that a client can call to get something done. When an API needs processing help from the client, it will require an argument that is a callback, a reference to some code located in the client that will provide the processing help. In a more functional programming style, the callback parameter may even be the code itself. (This is where lambdas come into play.)
Sorting is good example of a .Net API that requires a callback. To sort a list of custom objects you have to know how to compare two of the objects and determine which will come before the other when sorted. The List.Sort() method in .Net has no way to know how you might want to compare two of your custom objects. So you write the comparison code yourself and when you call the Sort method you send an argument that is a reference to your comparison method. When List.Sort is running through it's alogorithm, it will call your method whenever it needs to compare two objects.
The two C# features that support callbacks are delegates and interfaces. When an interface is used, the client passes an instance of a class that has implemented a particular interface. When a delegate is used, the argument is a delegate type that refers either to a named method in the application or an anonymous method, possibly expressed with lambda syntax. So in the example below I'll show four ways of passing a callback argument: one to show how to do it with an interface and three more to show the three different ways to express a delegate.
Here's what the example will show:
- callback argument as an object that implements an interface
- callback argument as a delegate that refers to a named method
- callback argument as a delegate that is an anonymus method
- callback argument as a delegate that is an anonymus method expressed in lambda syntax
Why Callbacks 
- Make libraries more useful to more clients: each client can tailor functionality to it's own needs.
- Separation of functionality between different layers of software. One layer may define a bit of functionality in general but it won't dictate how it is carried out. Other layers that call on it will define their own implementation.
- Decoupling and clean design. If you've got a method with if statements to condition functionality for different objects, you might be able to move the code that's specific to each object into the object itself and call back to it from the method.
- Decoupling and code reuse. A library is an example of a layered design; it's uncoupled from any of it's clients, existing on it's own.
C# Callback with an Interface 
Interface polymorphism refers to the fact that an instance of any class that inherits an interface can be referred to using the name of that interface. This C# mechanism allows a library to use the same line of code to call back to a multitude of clients.
Here's how coding of a library and a client might go, assuming you're writing your own library:
- You decide to design your library so that it requires some processing help from it's clients.
- You create an interface type to act as both a contract between the library and the clients as well as a way to call back to the clients. Let's name it IApiInterface, that defines a method, DoTailoredWork()
- You're writing a program and decide you can use the services of a library.
- You write a class that inherits the required interface and implements all of it's methods. Let's name it MyClass, that inherits IApiInterface and implements DoTailoredWork(). Library documentation and intellisense help you figure out what interface you need to implement and how to use the library.
- You write the library API call, sending as a argument a reference to your class or one of it's methods. To do this, you instantiate a MyClass object and maybe call it myClassObj.
Below, MyClass makes a call to the Library, sending a reference to itself. The Library calls back to MyClass using that reference but refers to it as a type of IApiInterface, not MyClass. Another class, say YourClass, if it inherited IApiInterface, could call the Library in exactly the same way. Normally, you'd probably code up a separate class to implement the IApiInterface and send it's reference instead of this but I wanted to keep the example short.
{
void DoTailoredWork();
}
class MyClass : IApiInterface
{
public void DoWork()
{
// Call the library and send it a reference to me
Library.LibraryAPI_InterfaceCallback(this);
}
// Implement the DoTailoredWork method of IApiInterface
public void DoTailoredWork()
{
Console.WriteLine("Doing Work Tailored by MyClass");
}
}
class YourClass : IApiInterface
{
public void DoWork()
{
// Call the library and send it a reference to me
Library.LibraryAPI_InterfaceCallback(this);
}
// Implement the DoTailoredWOrk method of IApiInterface
public void DoTailoredWork()
{
Console.WriteLine("Doing Work Tailored by YourClass");
}
}
public static class Library
{
public static void LibraryAPI_InterfaceCallback(IApiInterface clientCallbackObject)
{
Console.WriteLine("Library doing it's work");
clientCallbackObject.DoTailoredWork();
}
}
See how the Library polymorphically refers to all it's clients as IApiInterface objects? The library knows that all client applications will have a method called DoTailoredWork() because the C# compiler enforces a so-called contract between the library and client, which is provided by the IApiInterface interface. When a class inherits an interface it must implement all the methods in that interface or the compiler will complain. A client that calls the API will not compile unless it implements the required interface.
C# Callback with a Delegate 
A delegate is a type that encapsulates a method. You could say it's a reference to a method but it's much more than that. It also defines the method's signature and so, is said to be type-safe. An instance of a particular delegate type can be used as a reference to any method that has the particular signature defined by the delegate. Like an interface object, a delegate can be used in a library to refer to a callback method. I have another article on delegates if you need more background by the time you finish this section.
{
public void DoWork()
{
// Call the library and send it a reference to my DoTailoredWork method
Library.LibraryAPI_DelegateCallback(DoTailoredWork);
}
public void DoTailoredWork()
{
Console.WriteLine("Doing Work Tailored by MyClass");
}
}
public static class Library
{
// Define a delegate type that can refer to any method that returns void and
// take no arguments.
public delegate void MyCallbackDelegate();
public static void LibraryAPI_DelegateCallback(MyCallbackDelegate clientMethod)
{
Console.WriteLine("Doing Library Work common to all clients. ");
clientMethod(); // Callback to client so it can do it's thing
}
}
The contract is enforced by the compiler, which insists that any method pointed to by a delegate instance have the signature of that delegate type. Notice that I defined a delegate, MyCallbackDelegate, that returns void and takes nothing. I could have used the Action delegate type that is built into the .Net framework but I wanted to declare my own delegate here in case you didn't know about Action. In the example below I do use Action and save the line of code that defines a custom delegate. The next paragraph talks a bit more about the delegate types that come with .Net. Skip it if you already know about them.
The library doesn't always have to define a delegate type because the .Net Framework's System namespace includes delegate types that define most method signatures you could need. I talk about them in my article on delegates. So, if for some reason your callback design requires a method with a custom signature, one that doesn't match anything in the .Net library, (though I can't really imagine you would becuase the .Net built-in options cover about everything you might need), then you have to define your own custom delegate type. Or you might want to define your own delegate type because you want your delegate name to be something meaningful. So we could have eliminated the definition of MyCustomDelegate and used System.Action instead. I only used the custom delegate definition for tutorial purposes; otherwise I would have used Action and saved a line of code.
As for the client, it doesn't necessarily need to supply a named method for the callback. An anonymous method could be used, especially if the method is short. The anonymous method could be expressed with lambda syntax, which is frequently done in .Net. You'll see this in the big example at the end of this article. Again, see delegates for more information on lambda expressions.
When to Use Delegates Instead of Interfaces 
I'll just link you over to MSDN to an article titled: When to Use Delegates Instead of Interfaces.
The Example 
The example has:
- A library
- An interface
- Two Clients
- As an interface object
- As a delegate pointing to a named method
- As a delegate in the form of an anoymous method
- As a delegate in the form of a lambda expression
Remember that a delegate instance encapsulating a named method, an anonymous method and a lambda statement are all delegates. Also, in Lambda syntax, when the method has no arguments, you pass empty parentheses: () =>. You can copy and paste the whole thing into a console app and have at it.
namespace Callbacks_In_C_Sharp
{
class Program
{
static void Main(string[] args)
{
// Create a client and demonstrate callbacks
Bob bob = new Bob();
bob.DoWork();
Console.WriteLine("");
// Create another client and demonstrate callbacks
Evelyn evelyn = new Evelyn();
evelyn.DoWork();
Console.ReadKey();
}
}
// Bob is a client of the Library. He inherits IContract and implements
// the TailoredWork method, where he tailors the work that the library does.
// To clarify, the client only needs to implement IContract.TailoredWork if it
// passes a callback in the form of an IContract object. If a client uses one
// of the delegate options it wouldn't need to inherit IContract.
class Bob : IContract
{
public void DoWork()
{
Console.WriteLine("=========== Bob doing work work. =============");
// Call into the Library with an IContract object
Library.DoLibraryWorkInterfaceCallback(this);
// Call into the Library with a delegate
Library.DoLibraryWorkDelegateCallback(TailoredWork);
// Call into the Library with an anonymous method
Library.DoLibraryWorkDelegateCallback(delegate
{
Console.WriteLine("Doing Work Tailored by Bob -Anonymous method ");
});
// Call into the Library with an anonymous method expressed in lambda syntax
Library.DoLibraryWorkDelegateCallback(() =>
Console.WriteLine("Doing Work Tailored by Bob - Lambda"));
}
// The code to tailor the functionality; Bob's implementation of
// IContract.TailoredWork(). In this example it's right inside the client
// class but often things are designed such that the code is in a separate class.
// Interface implementations must be public so the Library can call it; a C# rule.
public void TailoredWork()
{
Console.WriteLine("Doing Work Tailored by Bob - Named Method");
}
}
// All comments for Bob - ditto for Evelyn
class Evelyn : IContract
{
public void DoWork()
{
Console.WriteLine("=========== Evelyn doing work =============");
Library.DoLibraryWorkInterfaceCallback(this);
Library.DoLibraryWorkDelegateCallback(TailoredWork);
Library.DoLibraryWorkDelegateCallback(delegate
{
Console.WriteLine("Doing Work Tailored by Evelyn - Anonymous Method");
});
Library.DoLibraryWorkDelegateCallback(() =>
Console.WriteLine("Doing Work Tailored by Evelyn - Lambda "));
}
public void TailoredWork()
{
Console.WriteLine("Doing Work Tailored by Evelyn - Named Method");
}
}
// Class library for clients to use.
// This library has various methods, each showing a different way that the
// client can provide a callback to the library.
public static class Library
{
public static void DoLibraryWorkInterfaceCallback(IContract clientObject)
{
Console.WriteLine("\r\n----- Interface -----\r\n");
Console.WriteLine("Doing Library Work common to all clients. ");
clientObject.TailoredWork();
}
public static void DoLibraryWorkDelegateCallback(Action clientMethod)
{
Console.WriteLine("\r\n----- Delegate -----\r\n");
Console.WriteLine("Doing Library Work common to all clients. ");
clientMethod();
}
}
// This interface serves as a contract between the Library and a client when
// a client passes a callback to the Library in the form of an object of
// this interface
public interface IContract
{
void TailoredWork();
}
}
Real World Examples in .Net : Sorting, Events, LINQ 
Three areas of .Net that rely heavily on callbacks using delegates are events, LINQ and Async programming. Events are simply callbacks supported by delegates, normally based on a convention in .Net which describes a particular signature for the callback method: returns void and takes two arguments, an object instance and an instance of an EventArgs class. I explain events in detail in another article: Custom Events. Many LINQ extension methods will take delegate arguments which are callbacks used to find out exactly how to do something, like which property to filter on in a Select on a list of custom objects. If you look at all the overrides for the various LINQ extension methods on the Enumerable class, you'll see that the generic overrides all take a delegate argument but the overrides for the various numerical C# data types don't.
The Average extension method on an Int32 doesn't take a delegate argument because averaging a list of integers is a well defined function. Here it is:this IEnumerable<int> source
)
this IEnumerable<TSource> source,
Func<TSource, decimal> selector
)
this IEnumerable<TSource> source,
Func<TSource, int> selector
)
double average = fruits.Average(s => s.Length);
Console.WriteLine("The average string length is {0}.", average);
In the introduction of this article I briefly explained how Sorting in .Net uses a callback. Sorting is a good example because Sort() gives you a choice between using an interface or a delegate for the callback argument; it has overrides for passing an IComparer object or a Comparison delegate as a callback. The callback is used to compare two objects and decide which should come before the other. The whole sorted [sic] story is in this article:Sorting A List Of Custom Objects.
Comments
Your explanation is very clear and good ! Especially the naming of the Class, methods etc. The output of the writeline is very precise, making it clear to the reader that what function the method is doing! Well done.
I like your article very much. All names of the Methods, members are so well thought, it really helps to understand the callback which I found almost impossible to get a handle on it. Thanks !!! Callback when explained by this article is like a song!
Great explanation, you clarified a lot of things for me, thanks!!
good article
Have been looking for a good article on the basics of callbacks for a day before I stumbled upon this.Its an excellent writeup and explains callbacks in a simple and clear manner.Thanks :)
(based on 14 reviews)