BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

C# Programming
.NET 1.1+

C# Event Accessors

Events can easily be added to classes to allow objects to indicate when an action occurs. Using standard declarations, multiple subscribers can be added to a single event. To increase the level of control over subscriptions, you can use event accessors.

Event Accessors

In a previous article we examined the use of events in C#. These provide a manner for objects to signal actions such as state changes or indications that an activity is about to be performed. The previous article explained how to create custom events and link them to one or more subscribers. The method used to subscribe to and unsubscribe from events utilised the compound assignment operators += and -=.

In most situations the compound assignment operator method for controlling subscription is adequate. However, sometimes additional control over subscription is required. For example, it can be useful for debugging and monitoring purposes to perform logging operations when an event is subscribed to. You may also wish to check permissions before allowing a subscription. In some cases you may even wish to replace the structure for storing subscriptions, though this is rare.

When you use the simple compound assignment operator method of subscription, the compiler automatically generates two event accessors. These are similar to the get and set accessors used with properties but are named add and remove. The add accessor registers a new subscription to an event and the remove accessor unregisters an existing one. When you wish to gain control over the implementation of an event, you can explicitly declare these accessors and the code within them.

Using Custom Event Accessors

To demonstrate the use of custom event accessors, the remainder of this article will implement a class that uses them. This class will represent a car with a single property containing the current speed. Whenever the speed changes, the event will be raised. To begin, we will create a class to hold the event arguments. This class will be available to the subscribers of the event and will define properties to hold the original speed and the new value.

To follow the examples, create a new console application. Create a class named "SpeedChangedEventArgs" and add the following code:

public class SpeedChangedEventArgs : EventArgs
{
    int _oldSpeed;
    int _newSpeed;

    public int OldSpeed
    {
        get
        {
            return _oldSpeed;
        }
        set
        {
            _oldSpeed = value;
        }
    }

    public int NewSpeed
    {
        get
        {
            return _newSpeed;
        }
        set
        {
            _newSpeed = value;
        }
    }
}

With the SpeedChangedEventArgs class created, we can declare the delegate that will be used by the event. Create a new class file named "Car" and add the following code outside of the class's code block.

NB: If no properties need to be passed to subscribers, you can omit the event arguments class and use the built-in EventHandler delegate instead of defining your own.

public delegate void SpeedChangedEventHandler(object source, SpeedChangedEventArgs e);

We can now add the code for the Car class as shown below. This code has several noteworthy elements. The first of these is the declaration of the SpeedChangedEventHandler object as a class-level variable. When using event accessors, such a variable is required.

The second key difference is the definition of the event. Instead of simply naming an event and specifying the delegate type, two code blocks are added for the add and remove accessors. Each accessor receives a variable named "value" that holds the details of the subscriber. In the sample code the add block adds the value to the event handler and the remove accessor unregisters the subscriber. Both accessors perform their tasks within a lock statement to ensure thread-safety.

In this example, the add and remove accessors provide similar functionality to an event declared without accessors. However, you can add further code within either accessor according to your requirements. For example, you could examine the "value" variable in the add accessor and decide not to register the subscription in some situations. You could also add code to output information when either accessor is executed.

public class Car
{
    SpeedChangedEventHandler _speedChangedEventHandler;
    object lockThis = new object();
    int _speed = 0;

    public event SpeedChangedEventHandler SpeedChanged
    {
        add
        {
            lock (lockThis)
            {
                _speedChangedEventHandler += value;
            }
        }
        remove
        {
            lock (lockThis)
            {
                _speedChangedEventHandler -= value;
            }
        }
    }

    public virtual void OnSpeedChanged(int oldSpeed, int newSpeed)
    {
        SpeedChangedEventArgs e = new SpeedChangedEventArgs();
        e.OldSpeed = oldSpeed;
        e.NewSpeed = newSpeed;

        if (_speedChangedEventHandler != null)
            _speedChangedEventHandler(this, e);
    }

    public int Speed
    {
        get
        {
            return _speed;
        }
        set
        {
            int oldSpeed = _speed;
            _speed = value;
            OnSpeedChanged(oldSpeed, _speed);
        }
    }
}

We can test the Car class and its event by adding code to the Main method of the console application's Program class. Replace the automatically generated Main method with the code below. The new Main method instantiates a Car object and sets the initial speed. The event is then linked to the CarSpeedChanged method so that when the speed is changed for a second time, the details are outputted to the console. Finally, the event subscription is removed and the final speed change causes no notification.

static void Main(string[] args)
{
    Car car = new Car();
    car.Speed = 25;
    car.SpeedChanged += new SpeedChangedEventHandler(CarSpeedChanged);
    car.Speed = 50;
    car.SpeedChanged -= new SpeedChangedEventHandler(CarSpeedChanged);
    car.Speed = 75;
}

static void CarSpeedChanged(object source, SpeedChangedEventArgs e)
{
    Console.WriteLine("Car speed changed from {0} to {1}",
    e.OldSpeed, e.NewSpeed);
}

/* OUTPUT

Car speed changed from 25 to 50

*/
26 August 2009