ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

A Java Programmer Looks at C# Delegates

by Steven M. Lewis and Wilhelm Fitzpatrick
05/21/2003

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

public class Class1 {
    ...
    public void show(String s) { .. }
}

public class Class2 {
    ...
    public void show(String s) { .. }
}

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

public class Class1 {
    ...
    public void show(String s) { .. }
}

public class Class2 {
    ...
    public void display(String s) { .. }
}

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:

C# Code

// define three classes with similar methods
// two instances with differing names, one static.

public class Class1 {
    public void show(String s) { Console.WriteLine(s); }
}

public class Class2 {
    public void display(String s) { Console.WriteLine(s); }
}

// allows static method as well
public class Class3 {
    public static void staticDisplay(String s) { Console.WriteLine(s); }
}

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:

C# Code

public class TestDelegate
{
    // define a datatype as a method taking a string returning void
    public delegate void doShow(String s);

    public static void Main(string[] args) 
    {
        // make an array of these methods
        doShow[] items = new doShow[3];

        items[0] = new doShow(new Class1().show);
        items[1] = new doShow(new Class2().display);
        items[2] = new doShow(Class3.staticDisplay);

        // call all items the same way
        for(int i = 0; i < items.Length; i++) {
            items[i]("Hello World");
        }
    }
}

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

items[1] = new doShow(new Class2().display);

Related Reading

Programming C#
By Jesse Liberty

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.

Pages: 1, 2

Next Pagearrow