figure 2.8 continued continued.78 chapter 2 • introducing c# programming // appears in the message queue. notice the signature matches // that requried by addeventcallback public void logaddrequest( string firstname, string lastname, string middlename, string ssn ) { string name = firstname + " " + middlename + " " + lastname; filestream stream = new filestream( m_filename, filemode.openorcreate, fileaccess.readwrite); streamwriter writer = new streamwriter( stream ); writer.basestream.seek( 0, seekorigin.end ); writer.write("{0} {1} /n", datetime.now.tolongtimestring(), datetime.now.tolongdatestring()); writer.write( "adding employee - name: {0}, ssn: {1}", name, ssn ); writer.write("/n------------------------------------/n/n"); writer.flush(); writer.close(); } } a new class, employeequeuelogger, has been added. it has a method logaddrequest, which logs requests to add employees to a log file. the important thing to note is that the logaddrequest method has a signature that matches the addeventcallback delegate signature. an instance of the logger is created in the constructor of employeequeuemonitor. the code that wires up the delegates is also in the constructor and is shown here: m_logger = new employeequeuelogger( "log.txt" ); m_addeventcallback = new addeventcallback( this.addemployee ); m_addeventcallback += new addeventcallback( m_logger.logaddrequest ); http://www.syngress.com figure 2.8 continued.introducing c# programming • chapter 2 79 first, a new logger instance is created. next, the delegate is initialized with a first callback function to the addemployee method of employeequeuemonitor. finally, a second callback is added to the delegate, which will invoke the logaddrequest of the employeequeuelogger class. notice that the plus sign is used to add the second callback to the delegate. the plus sign (addition operator) has been overloaded in the system.delegate class of the .net framework to call the combine method of that class.the combine method adds the callback to the list of methods the delegate maintains.the minus sign (subtraction operator) is also overloaded to call the remove method, which removes a callback from the list of methods the delegate maintains.the rest of the source code remains unchanged. when the delegate is invoked in the start method of employeequeuemonitor, both employeequeuemonitor.addemployee and employeequeuelogger.logaddrequest are executed. events the event model is often referred to as the publish/subscribe model or the listener pattern. the idea behind the event model is that a class publishes the events that it can raise. consumers of the class object subscribe to the events they are interested in.when the event occurs, the object that monitors the event notifies all sub-scribers that the event has been raised.the subscribers then take some action. the event model is often used in gui programs. handlers are set up for common events, such as pressing a button.when the button press event occurs, all subscribers registered for the button press event are invoked.the .net framework uses the event model and in particular the system.event delegate for windows forms–based applications. the .net framework supplies a built in delegate of type system.event. the idea of events in the .net framework is to supply a single signature for the del-egate regardless of the data that is passed to the subscribed callback. one of the arguments for the event delegate is an object derived from the .net framework class system.eventargs, which contains the data the callback needs.you declare a class derived from system.eventargs with the data your callback needs.when the event takes place, you instantiate your derived eventargs object and invoke the event. callback functions subscribed to the event are called passing the object derived from eventargs. changes to the multicast delegate code sample that implement events are shown in figure 2.9.the full source code for this sample is on the cd in the file events.cs. http://www.syngress.com.80 chapter 2 • introducing c# programming figure 2.9 relevant portions of the events.cs program listing /// <summary> /// defines the data that will be passed from the event delegate to /// the callback method when the event is raised /// </summary> class addemployeeventargs : eventargs { string m_firstname; string m_lastname; string m_middlename; string m_ssn; public addemployeeventargs( string firstname, string lastname, string middlename, string ssn ) { m_firstname = firstname; m_lastname = lastname; m_middlename = middlename; m_ssn = ssn; } // event argument properties contain the data to pass to the // callback methods subscribed to the event. public string firstname { get { return m_firstname; } } public string lastname { get { return m_lastname; } } public string middlename {get { return m_middlename; } } public string ssn { get { return m_ssn; } } } /// <summary> /// simulates monitoring a message queue. when a message appears /// the event is raised and methods subscribed to the event // are invoked. /// </summary> http://www.syngress.com continued.introducing c# programming • chapter 2 81 class employeequeuemonitor { // event signature for addemployeeevent public delegate void addemployeeevent( object sender, addemployeeventargs e ); // instance of the addemployeeevent public event addemployeeevent onaddemployee; private employeequeuelogger m_logger; private employees m_employees; private int m_lengthqueue; private string[, ] m_msgqueue = { {"timothy", "arthur", "tucker", "555-55-5555"}, {"sally", "bess", "jones", "666-66-6666" }, {"jeff", "michael", "simms", "777-77-7777"}, {"janice", "anne", "best", "888-88-8888" } }; public employeequeuemonitor( employees employees ) { m_employees = employees; m_lengthqueue = 4; m_logger = new employeequeuelogger( "log.txt" ); // register the methods that the event will invoke when an add // employee message is read from the message queue onaddemployee += new addemployeeevent( this.addemployee ); http://www.syngress.com figure 2.9 continued continued.82 chapter 2 • introducing c# programming onaddemployee += new addemployeeevent( m_logger.logaddrequest ); } // drain the queue. public void start() { if ( m_employees == null ) return; for ( int i = 0; i < m_lengthqueue; i++ ) { // pop an add employee request off the queue string firstname = m_msgqueue[i,0]; string middlename = m_msgqueue[i,1]; string lastname = m_msgqueue[i,2]; string ssn = m_msgqueue[i,3]; console.writeline( "invoking delegate" ); // create the event arguments to pass to the methods // subscribed to the event and then invoke event resulting // in the callbacks methods being executed, namely // employees.this.addemployee() and // employeequeuelogger.logaddrequest() addemployeeventargs args = new addemployeeventargs( firstname, lastname, middlename, ssn ); onaddemployee( this, args ); } } public void stop() { http://www.syngress.com figure 2.9 continued continued.introducing c# programming • chapter 2 83 // in a real communications program you would shut down // gracefully. } // called by event whenever a new add employee message appears // in the message queue. notice the signature matches that required // by system.event public void addemployee( object sender, addemployeeventargs e ) { console.writeline( "in delegate, adding employee/r/n" ); int index = m_employees.length; m_employees[index] = new employee ( e.firstname, e.middlename, e.lastname, e.ssn ); } } /// <summary> /// writes add employee events to a log file. /// </summary> class employeequeuelogger { string m_filename; public employeequeuelogger( string filename ) { m_filename = filename; } // called by event whenever a new add employee message appears // in the message queue. notice the signature matches that required // by system.event public void logaddrequest( object sender, addemployeeventargs e ) http://www.syngress.com figure 2.9 continued continued.84 chapter 2 • introducing c# programming { string name = e.firstname + " " + e.middlename + " " + e.lastname; filestream stream = new filestream( m_filename, filemode.openorcreate, fileaccess.readwrite); streamwriter writer = new streamwriter( stream ); writer.basestream.seek( 0, seekorigin.end ); writer.write("{0} {1} /n", datetime.now.tolongtimestring(), datetime.now.tolongdatestring()); writer.write( "adding employee - name: {0}, ssn: {1}", name, e.ssn ); writer.write("/n------------------------------------/n/n"); writer.flush(); writer.close(); } } a new class, addemployeeventargs, has been added. it contains the informa-tion that will be passed to callback methods subscribed to the event. notice the data members of the addemployeeventargs class are the same as the signature for the addeventcallback delegate in our previous sample. instead of invoking the callback with individual arguments, when using events, you pass a class object, which contains the arguments instead. just as with the delegates samples, we declare the signature and create a member variable for the delegate in employeequeuemonitor class. the only differ-ence is that the signature matches the signature necessary for events.the first parameter is the object that raised the event, and the second is the object instance that contains the arguments passed to subscribed callback methods.this is shown here: public delegate void addemployeeevent( object sender, http://www.syngress.com figure 2.9 continued.introducing c# programming • chapter 2 85 addemployeeventargs e ); public event addemployeeevent onaddemployee; in the constructor of the class, we subscribe the callback methods to the event as shown here: onaddemployee += new addemployeeevent( this.addemployee ); onaddemployee += new addemployeeevent( m_logger.logaddrequest ); the callback methods have the correct signature for event callbacks. here are the callback method’s signatures: public void addemployee( object sender, addemployeeventargs e ) public void logaddrequest( object sender, addemployeeventargs e ) when an add employee message is popped off the queue in the start method of employeequeuemonitor, an instance of the addemployeeeventargs is created and the event is invoked. here is the code that accomplishes this: addemployeeventargs args = new addemployeeventargs( firstname, lastname, middlename, ssn ); onaddemployee( this, args ); as you can see, using events instead of delegates is really just a syntactic dif-ference. the code is nearly identical.the main benefit is that you don’t have a different delegate signature for every delegate you create based on the data that is passed to subscribed callbacks. instead, the standard event delegate signature will suffice.