Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

C#: A language alternative or just J--?, Part 2

The semantic differences and design choices between C# and Java

  • Print
  • Feedback

Page 4 of 5

You may have heard that property accessors are simply methods used for accessing objects' internal variables. This idea is a complete misunderstanding of what property accessors truly are. While property accessors often, even usually, simply set and return the value of a private variable, property accessors are most useful when they return a calculated or deferred value. What's more, a property accessor that today returns the value of an internal variable might tomorrow return a value it got from somewhere else, if the class's implementation changed. The classes that access the property don't need to know where the property value came from, as they would if they were accessing an internal variable. Property accessors cannot break encapsulation in the way that public field accessors do. They are simply not the same thing. Whether defined by convention as in Java or built into a language like C#, properties are an excellent way to improve decoupling between classes.

Delegates

Delegates are C#'s answer to C++ function pointers, except that delegates are safe, and function pointers are dangerous. Both Java and C# have done away with function pointers, finding safer ways to maintain references to behavior that is determined at runtime (which is what function pointers are used for in C++).

C# borrowed the concept of delegates from Delphi and Visual J++. A delegate is a reference to a method, plus either a class or an instance against which the method can be called, depending on whether or not the method is static. Delegates are useful whenever behavior needs to be deferred to another object. For example, a sort algorithm might take a delegate as one of its arguments:

// Declaration of the delegate
delegate int Comparator(object a, object b);
class Quicksort {
   static void sort(Comparator c, object[] 
   objectsToSort) {
      // ... quicksort logic leading to a
comparison
         if (c(objectsToSort[left], 
         objectsToSort[pivot]) > 0) {
            // recursive call...
         } else { // ...and so on...
   } };


In the preceding code you see the delegate called Comparator declared and then used as an argument in Quicksort.sort(), which is static. In the "if" statement, the delegate c is called just as if it were a method. This is equivalent to calling through a function pointer in C, except the delegate is type-safe and can't possibly have an illegal value.

The typical Java approach usually involves implementing an interface, like this:

public interface Comparator {
   int compare(Object a, Object b);
}
public class Quicksort  {
   public static void sort(Comparator c, Object[] 
   objectsToSort) {
         // ...quicksort logic...
         if (c.compare(objectsToSort[left], 
         objectsToSort[pivot]) > 0) {
         // ... and so on
      }
   }
};


The object implementing Comparator in the preceding code will commonly be in an inner or even anonymous class. Again, as in the case of properties, the results in Java and C# are quite similar, but the C# syntax is slightly clearer. Delegates are more than just syntactic shorthand for interface calls, however. A delegate can maintain a reference both to a method and to an instance upon which that method can be invoked. You'll see an example of this usage in the next section on events. Also, you'll find a link to a Sun whitepaper on delegates in Resources.

Events and event notification

C# events operate much like Java 1.1+ events, except that C#'s are integrated into the language. For a class to receive an event, it must have a field or property that is a delegate marked with the event keyword. From inside the class, this delegate member is just like any other delegate: it can be called, checked to see if it's null, and so forth. From outside the class, however, there are only two things you can do with it: add or remove a method to the delegate, using the operator+= or the operator-=, respectively. Event delegates apparently act as collections or lists of references to methods and instances.

In essence, an event delegate is a list of methods to be called when the delegate is invoked. In my opinion, this usage is extremely confusing, since in this particular case delegates act like a collection of method pointers, rather than a single method pointer. The language spec does, however, specify that all delegates define addition and subtraction operators, implying that any delegate may be used in this way.

For example, here is the definition of part of a class called MyButton, which can respond to mouse clicks:

public delegate void ClickHandler(object source, 
Event e);
public class MyButton: Control {
   public event ClickHandler HandlerList;
   protected void OnClick(Event e) {
      if (HandlerList != null) {
         HandlerList(this, e);
      }
   }
   //... override other methods of Control...
}


A dialog class containing one of these buttons could then register itself to receive clicks from the button, adding one of its methods to the button's HandlerList with the operator+=, like so:

public class OneWayDialog: Control {
   protected MyButton DialPhoneButton;
   public OneWayDialog() {
      DialPhoneButton = new MyButton("Dial Phone");
      DialPhoneButton.HandlerList += new 
      ClickHandler(DoDial);
   }
   public void DoDial(object source, Event e) {
      // Call method that dials phone
number
   }
}


When a OneWayDialog is created, it creates a MyButton and then adds a delegate to its method DoDial() to the MyButton's HandlerList. When the user clicks the MyButton, the framework calls the MyButton's OnClick() method, which then invokes the HandlerList delegate, resulting in a callback to OneWayDialog.DoDial() against the original OneWayDialog instance.

In Java, this sort of mechanism (called event multicasting) is handled with lists of objects that implement event listener interfaces. Manipulation of these lists of interfaces must be done manually, although there are "support" classes that do it for you. C# manages these "listener lists" under the hood by building them into the delegate's semantics. While writing code that maintains listener lists in Java can be a bit tedious, at least it's clear what's going on. C# "simplifies" the handling of these lists by building it into the language, but at the cost of creating a language feature (the delegate) that behaves like a method pointer sometimes and like a collection of method pointers at other times. This may just be a matter of taste, but personally I think the delegates' semantics are messy and confusing. On the other hand, not everyone fully understands Java event listener interfaces the first time.

  • Print
  • Feedback

Resources