C#, Java and Events

Tags: .NET, C#, Events, Java

A couple of days ago, we were talking about events in C#.

Specifically:

a) Why Microsoft went to the effort to add the 'event' keyword to the C#

language when the same functionality can be accomplished using a class and interface.

b) Why all event argument objects must inherit from the 'EventArgs' oabject.

For the uninitiated, .NET events are essentially delegates (think of them as function pointers) which allow you to register for a callback when something happens. This makes it easy to write code according to the Observer Pattern. For example, in Windows Forms, the 'Button' class has a "Click" event which is called when the button is physically clicked. Attaching to this event allows you to have a certain method run whenever that happens, e.g.

public class Form1
{
    private Button button1;

    public Form1()
    {
        this.button1 = new Button();
        // other code to perform actions such as registering the button on the form
        this.Click += new System.EventHandler(this.Button1_Click);
    }

    private void Button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show("Button 1 Clicked");
    }
}

The above code snippet can be simplified using a lambda expression, but we'll leave that for another post.

Why all event argument objects must inherit from 'EventArgs'

After some research, the reason for all event arguments having to inherit from the 'EventArgs' object isn't totally clear, but it seems that Microsoft wanted them all to inherit from a common base object to allow them to add additional parameters to all event arguments at a later date by simply modifying the base class. At present, this design decision doesn't appear to offer any

benefits but does have some drawbacks since enums cannot inherit from 'EventArgs' meaning that you would need to write an 'EventArgs' based class containing an instance of an enum even if all you needed to pass back with you event was that enum. A common and reasonably trivial work-around for this is to write a generics 'EventArgs<T>' class:

public class EventArgs<T> : EventArgs
{
    public EventArgs(T value)
    {
        this.Value = value;
    }

    public T Value { get; set; }
}

Why Microsoft went to the effort to add the 'event' keyword to the C# language when the same functionality can be accomplished in Java using just an interface

Say we wanted to write a C# class with event called "Started" that returns a

string, we could write the following:

class Program
{
    internal event EventHandler<EventArgs<string>> Initialised;

    static void Main(string[] args)
    {
        Program program = new Program();
        program.Initialised += program.Program_Initialised;
        program.OnInitialise();
    }

    private void OnInitialise()
    {
        if (this.Initialised != null)
        {
            this.Initialised(this, new EventArgs("In Initialised"));
        }
    }

    private void Program_Initialised(object sender, EventArgs e)
    {
        Console.WriteLine(e.EventData);
    }
}

To accomplish the same thing in Java we would need to write some library code which can mimic the event keyword and manage event listeners. Here is a fairly simple implementation (which will be improved in future posts!):

public interface EventListener {
    public void onNotify(S source, M message);
}

public class Event {

    private Object[] listeners;

    public void add(final EventListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("Listener cannot be null");
        }

        synchronized (this) {
            if (listeners == null) {
                listeners = new Object[] { listener };
            } else {
                listeners = Arrays.copyOf(listeners, listeners.length + 1);
                listeners[listeners.length - 1] = listener;
            }
        }
    }

    public void dispatch(final S source, final M message) {
        if (source == null) {
            throw new IllegalArgumentException("Source cannot be null");
        }

        final Object[] local = listeners;
        if (local != null) {
            for (final Object object : local) {
                final EventListener listener = (EventListener) object;
                listener.onNotify(source, message);
            }
        }
    }
}

Once all this is in place, we can write the Java class to use the events:

public class EventTest {

    public Event onInitialised = new Event();

    public static void main(final String[] args) {

        final EventTest eventTest = new EventTest();

        eventTest.onInitialised.add(new EventListener() {
            public void onNotify(final EventTest source, final String message) {
                System.out.println(message);
            }
        });

        eventTest.initialise();
    }

    public void initialise() {
        onInitialised.dispatch(this, "In initialised");
    }
}

So the main difference between the Java and C# implementation is that in C# the code equivalent to our 'Event' and 'EventListener' is all built in. Behind the scenes the C# compiler replaces an event declaration such as:

public event EventHandler Blah;

with:

private EventHandler Blah;
public void add_Blah(EventHandler someDelegate);
public void remove_Blah(EventHandler someDelegate);

The 'EventHandler' itself is private 'MulticastDelegate' that can have multiple "registrations" through the internal use of a linked list. By making the 'EventHandler' private, but having public methods to add and remove delegates from the chain, we achieve better encapsulation in that only the class which "owns" the event can "fire" that event.

Our next thought was about whether something similar could be achieved in Java, albeit without using a keyword. The following code shows it is possible by providing a public method allowing listeners to subscribe to the event, but keeping the event itself private. In this way the event can only be triggered by the owning class, and yet anyone can subscribe.

public class EventTest {
    private Event onInitialised = new Event();

    public void onInitialised(final EventListener listener) {
        onInitialised.add(listener);
    }
}

This also goes some way to addressing the final advantage that the 'event' keyword in C# offers: the ability to define events in interfaces. Although the 'Event' class in Java is not a suitable candidate to describe events provided by implementing classes, the public subscription method is:

public interface HasLifecycleEvents {
    public void onInitialised(final EventListener listener);
}

public class EventTest implements HasLifecycleEvents {
    private Event onInitialised = new Event();

    public void onInitialised(final EventListener listener) {
        onInitialised.add(listener);
    }
}

Next time... Improvements to our Java Events Implementation

Add a Comment

© 2012 Simbiosys Limited. All rights reserved.