A Java Programmer Looks at C# Delegates

Abstract

While C# has a set of capabilities similar to Java, it has added several new
and interesting features. Delegation is the ability to treat a method as a
first-class object. A C# delegate is used where Java developers would use an
interface with a single method. In this article, the use of delegates in C# is discussed, and code
is presented for a Java Delegate object that can perform a similar function. Download the source code here.

C#, pronounced C Sharp, is a language developed by Microsoft as a part of
its .NET initiative. The language is extremely similar to Java. Were it not
for legal difficulties between Microsoft and Sun, there is little question that
Microsoft would have chosen Java to fill the role in its plans that is
currently held by C#. The major features C# has in common with Java include
garbage collection, a virtual machine, single inheritance, interfaces, packages
(called namespaces), and the fact that all code is encapsulated within objects.

There are also a few significant differences between the two languages.
Because the developers of C# had the advantage of carefully examining Java
while developing their language, it is not surprising that some of the
differences attempt to address significant problems that are difficult to deal
with in Java. This article focuses on one item that Microsoft added to C#, why
it was added, and how similar functionality could be implemented in Java.

Delegates in C#

C# introduces the concept of delegates, which represent methods that are
callable without knowledge of the target object. Consider these situations:

Java Code

Here, two classes share a common method, show, performing a
similar function, display of data. We would like to be able to call that method
in the same way for Class1 and Class2. If the two
classes share a common interface, we can simply treat instances of either class
as instances of that interface. Unfortunately, if the classes do not share an
interface, as in the above example, there is no easy way to make a uniform
call. If the developer controls the code for both classes, it is possible to
retrofit a common interface. When one or both classes are in library code,
there is no easy fix.

A more complex situation is illustrated in the example below:

Java Code

These two classes have the methods show and display,
which perform a similar function and have a similar signature. That is, they
take similar arguments, return similar data, and could be used in a loop
doing conceptually similar things. However, because the names of the two
methods are different, no interface will recognize the two as performing the
same action.

Java developers may address these issues through reflection (by generating
an interface and implementing it with inner wrapper classes) or by constructing
a dynamic proxy. All of these solutions are somewhat clumsy and require
non-trivial amounts of code.

Consider how C# would address this issue. In the sample below, we define
three classes that implement similar methods, two with different names and a
third with a static implementing method:

We will now define a new data type, doShow, which abstracts the
common features of the methods in all three classes. That is, they all take a
single string as an argument and return void. This is done using the C#
delegate keyword.

C# Code

public delegate void doShow(String s);

Think of a delegate as an interface declaring exactly one method. An
instantiation of a delegate is similar to an anonymous inner class that
implements the interface through a one-line call to a single method (static or
instance) with a compatible signature.

With this new data type in hand, we may now arrange to invoke all three
methods via a common abstraction:

The main function creates an array populated with newly instantiated
doShow objects. These refer to the various instance and class
methods defined above. Let's take a closer look at the instantiation of a
delegate:

C# Code

In C#, a method is a first-class object in the same way a Class
like String.class is a first-class object in Java. In C#, if we
reference a method on an object (omitting the parentheses that would normally
signal a method call), C# instead treats the method name like a field, returning
the object representing that method. The constructor of a C# delegate expects
to be called with just such a method object.

In Java terms, C# is dynamically creating an interface that declares a
single method. When one considers how many interfaces (especially listeners and
other event handlers) fit this description, the utility of this approach is
apparent. C# uses delegates rather than Listener interfaces to
implement most of its event handling. Threads in C# are constructed with
delegates (System.Threading.ThreadStart), unlike Java, which uses
the Runnable interface.

When an instance of a delegate is constructed, the actions the compiler
takes are similar to the Java equivalent of building a wrapper class. This
wrapper class exposes the interface defined by the declaration of the delegate,
implementing the interface by calling the method that was passed to the
delegate constructor.

The C# approach requires hooks into the compiler not available in Java.
These hooks allow methods to be accessed at compile time via a convenient
syntax in much the same way we would write String.class to access
a known class at compile time. This approach allows the C# compiler to perform
type checking on the arguments to a delegate invocation when compiling it,
rather than throwing a type error at runtime.