THE CRANK HOUSE

Home
RSS Reader
Monday Night Golf
HowTo's
Scott Moriarty
Crankhouse Classic
Tour of Worcester Pics
Site Overview

Downloads

Download Page
NTimer
scheck

Friends

Bill Rowell
Kyle Bradshaw
Dan Pickett

Important Links

The Basics of UNIX Philosophy

Bad Vista

C# - Using Events Correctly

This is more of a rant than a howto, but it will also show "how to" use events correctly. I have been noticing a lot of bad usage, or exposure, of events to the public in some code I have been working with.

I am assuming you know the difference, in C#, between a delegate and event- as well as when to use a delegate and when to use an event. I am not a Microsoft MVP or what ever, but I have been programming in C# since 2002ish.

Defining an Event

Before you can go create an event, there must be a delegate type for the event you are going to fire. So say we have a really simple Robot class, from a super high-tech class libary that looks like this.

using System;

namespace CrankHouse.Awesome
{
	public class Robot
	{
		private bool isOn;
		private int robotId;
		private static int ROBOT_COUNT;

		public Robot()
		{
			ROBOT_COUNT++;
			robotId = ROBOT_COUNT;
			isOn = false;
		}

		// Turn the robot on.	
		public void PowerUp()
		{
			inOn = true;
		}

		// Turn the robot off.
		public void PowerDown()
		{
			inOn = false;
		}

		public bool IsOn
		{
			get { return isOn; }
		}

		public int RobotID
		{
			get{ return robotId; }
		}
	}
}
	

The Delegate

So let's say that sometime we might want to let other classes know when the state of our robot has changed. For example, other objects might be interested in knowing when a Robot object is turned on or off. First we have to declare the the delegate for that type of action.

namespace CrankHouse.Awesome
{
	public delegate void RobotPowerChangedHandler( object sender, System.EventArgs e );
}
		

The Event

Great, that did nothing for the Robot class yet, because it doesn't define any members of type "RobotPowerChangeHandler" ( a delegate is basically a type in .NET as is the Robot class ). I am going to skip a bunch, but I am going to add an event(s) to the Robot class. I want to give classes to ability to listen in to when the state ( on/off ) of a Robot object changes. To do that is pretty simple.
We just add this to to Robot class:

public class Robot 
{
	private event RobotPowerChangedHandler onPowerChanged;
	.
	.
	.
	public event RobotPowerChangedHandler OnPowerChanged
	{
		add
		{
			onPowerChanged += value;
		}
		remove
		{
			onPowerChanged -= value;
		}
	}
}
	

Now we have given other classes the ability to listen for changes in the state of the power of an instance of our Robot class. Right now, nothing would happen because the event is never fired by the Robot class. I am going to add the code to fire the event when the Robot is turned on. It's the same for when it's turned off. So I add this to the PowerUp() and PowerDown() methods:

	.
	public void PowerUp()
	{
		isOn = true;
		if( onPowerChanged != null )
		{
			EventArgs ea = new EventArgs();
			onPowerChanged(this, ea);
		}
	}
	.
	

Subscribing to an Event

So now let's say we have a RobotManager class that does some awesome managing of instances of the death defying Robot class. The RobotManager would like to track when Robot's are turned on and off. We might have a method of the RobotManager class that returns and fully operational imperial Robot like this, and has it do something wild:

public Robot CreateAwesomebot()
{
	Robot robo = new Robot();
	// Subscribe to the OnPowerChanged event.
	robot.OnPowerChanged += new RobotPowerChangedHandler(LogPowerChanged); 
	return robot;
}
// Method to listen	for when the Robot object has been powered up or down.
private void  LogPowerChanged( object sender, System.EventArgs e )
{
	Robot r = sender as Robot;
	StreamWriter sw = File.AppendText("robot_power.log");
	if( r.IsOn )
		sw.WriteLine("Robot {0} has been powered up.", r.RobotID);
	else
		sw.WriteLine("Robot {0} has powered down.", r.RobotID);

	sw.Flush();
	sw.Close();
}
	

So now I have subscribed the Robot's OnPowerChanged event in the CreateAwesomebot method. So when a Robot, created by that method, has been powered up or down the OnPowerChanged event is fired and the RobotManager's LogPowerChanged method will log it.

If you know how to use delegates you will see that I am doing just that, I created the LogPowerChanged method. The method I created has the same method signature ( return type and parameter types ), as the RobotPowerChangedHandler delegate. I then use the event-property to add my event handle to actually subscribe to the event. By using the "+=" operator and the event/add/remove keywords it is treated a little differently. The event type in C# overloads the "+=" operator and allows classes to add ( and remove ) event handlers.

Clients Should Not Fire the Events!

It really annoys me when someone doesn't stick to the rules of encapsulation, especially when the language makes it so easy to stick to.
I have been seeing the following pattern ( a lot ) in the code I am currently working with. The events are declared as public. So my (WRONG)declaration would look something like this:

	public event RobotPowerChangedHandler onPowerChanged;
		

So now JoeBlow can fire the onPowerChanged event of the Robot regardless of the state. Let's say for a minute that I did declare the event the wrong way. Now instead of subscribing to the event the way it should be done, any instance can fire the onPowerChanged event, even if the power state has not changed. All objects that did subscribe to the event are being lied to.

public class Foo
{
	public static void Main(string [] args)
	{
		RobotManager rm = new RobotManager();
		Robot robot = rm.CreateAwesomebot();

		robot.onPowerChanged(new Robot(), new System.EventArgs()); // The power state really hasn't changed so the event should not be fired.
		robot.onPowerChanged(new Robot(), new System.EventArgs()); // The power state really hasn't changed so the event should not be fired.
		robot.onPowerChanged(new Robot(), new System.EventArgs()); // The power state really hasn't changed so the event should not be fired.

		robot.PowerUp(); // Now the event should be fired! 
	}
}
	

Now the log file of the RobotManager has a 3 messages stating the the Robot's power state has changed, when it hasn't! It's really no one's fault except the person who wrote the shitty code in the Robot class allowing client classes to falsely fire the onPowerChanged event.
That, to me, violates encapsulation for two reasons:

  1. The client class should not have access to the onPowerChanged event, it should only be fired when the PowerUp() method is called. This can lead to inconsistencies in the expected behavior of the events.
  2. It shouldn't be the responsibilty of the client class to fire those events. By declaring the event the Robot class is basically saying, "When my power state has changed, I will let you know. You don't tell me". Now, thanks to the Foo class, the RobotManager is getting false information. By looking at the log file, how can we truly know what Robot's are really on or off?

Imagine if on the codebehind of an ASP.NET web form, the Button class exposed the OnClick event the wrong way? I could be clicking buttons all over the place, even if they weren't clicked! Annoying, very annoying.


Valid CSS! Valid XHTML 1.0! Firefox 2 Apache httpd 2.0
Contact: joe at crankhouse d0t c0m
. . . . .