LA.NET [EN]

Nov 16

I guess we all know about events, right? Even so, I’ve decided to write a couple of posts about it and today I’ll be talking about some basic stuff associated with event definition and usage. So, what is an event? An event allows a type to notify other objects about something special which happened. If you’re a .NET developer, you know that events are everywhere. For instance, if you look at the Windows Forms’ controls, you’ll see events everywhere (ex.: who hasn’t handled the Click event generated by a Button?).

When a type exposes an event, it means that:

  • it allows another type to register a method which receives future notifications.
  • it allows another type to cancel a previous subscription.
  • it is responsible for notifying all the previous registered methods.

The CLR event model is based on delegates (which allows you to call methods in a type safe way – I guess I’ll return do delegates in future posts). Lets start with a simple example which assumes we have a simple Student type which generates an event in response to a change of its Name property (I’ll call it StudentNameChanges event). Do notice that in the real world I’d simply implement the INotifyPropertyChanged interface to signal this change. Since I want to present all the steps associated with creating an event, I’ll go with my own custom event…

When we expose an event, you must start by deciding if you’ll need to pass custom data to the methods that handle the event. In this case, I’ve decided to pass the old and new name values. In practice, this means that I’ll need to create a new type,derived from EventArgs (this is a convention),which exposes two properties: OldName and NewName.

public class StudentNameChangedEventArgs:EventArgs {
    public String OldName { get; private set; }
    public String NewName { get; private set; }

    public StudentNameChangedEventArgs( string oldName, string newName ) {
        OldName = oldName;
        NewName = newName;
    }
}

As I’ve said, using EventArgs as base is only a convention which you should follow. Nothing prevents you from passing a non-EventArgs type to a method that handles an event (though you’re probably going against what’s expected, which is not a good thing). Now, we’re ready to define the event member. The easies way to do this is to add a public field to our class:

public class Student {
    public event EventHandler<StudentNameChangedEventArgs> StudentNameChanged;
}

As you can see, an event is always declared through the event keyword, followed by the expected delegate type. In this case, and since our event args class inherits from EventArgs, we can reuse the EventHandler<T> type. After adding the field, it’s also expected to find a virtual protected method which is responsible for firing the event. Here’s the class’ complete code:

public class Student {
    private String _name;
    public String Name {
        get { return _name; }
        set {
            if (value == _name) return;
            var oldName = _name;
            _name = value;
            OnNameChanged( new StudentNameChangedEventArgs(oldName, _name) );
        }
    }
    protected virtual void OnNameChanged(StudentNameChangedEventArgs e) {
        if( StudentNameChanged != null ) {
            StudentNameChanged( this, e );
        }
    }
    public event EventHandler<StudentNameChangedEventArgs> StudentNameChanged;
}

The OnNameChanged method starts by checking the StudentNameChanged event field. When it’s not null, it will call all interested parties by passing a reference to the Student instance responsible for the event and the custom EventArgs parameter it received. The previous snippet also shows how the event gets generated. As you can see, it will always be generated from the setter of the Name property.

Now, let’s see how we can consume this event from our C# code:

var std = new Student( );
std.StudentNameChanged +=
    ( sender, e ) => Console.WriteLine( "{0} — {1}", e.OldName, e.NewName );
std.Name = "Luis";

Experienced developers will probably detect several things which can be improved in our previous snippets. For instance, using lambda expressions are great, but only  if you don’t need to cancel the subscription. Anyway, I’ll leave this improvements to the next post of the series. Stay tuned for more.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>