Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Notify registered objects that the event has occurred

By convention, the class should define a protected, virtual method that is called by code internally within the class and its derived classes when the event is to be raised. This method takes one param- eter, a NewMailEventArgs object, which includes the information passed to the objects receiving the notification. The default implementation of this method simply checks if any objects have registered interest in the event and, if so, the event will be raised, thereby notifying the registered methods that the event has occurred. Here is what the method in our MailManager class looks like.

 

internal class MailManager {

...

// Step #3: Define a method responsible for raising the event

// to notify registered objects that the event has occurred

// If this class is sealed, make this method private and nonvirtual protected virtual void OnNewMail(NewMailEventArgs e) {

 

// Copy a reference to the delegate field now into a temporary field for thread safety EventHandler<NewMailEventArgs> temp = Volatile.Read(ref NewMail);

 

// If any methods registered interest with our event, notify them if (temp != null) temp(this, e);

}

...

}


 

Raising an Event in a Thread-Safe Way

When the .NET Framework first shipped, the recommended way for developers to raise an

event was by using code similar to the following.

 

// Version 1

protected virtual void OnNewMail(NewMailEventArgs e) { if (NewMail != null) NewMail(this, e);

}

 

The problem with the OnNewMail method is that the thread could see that NewMail is not null, and then, just before invoking NewMail, another thread could remove a delegate from the chain making NewMail null, resulting in a NullReferenceException being thrown. To fix this race condition, many developers write the OnNewMail method as follows.

// Version 2

protected virtual void OnNewMail(NewMailEventArgs e) { EventHandler<NewMailEventArgs> temp = NewMail;

if (temp != null) temp(this, e);

}

 

The thinking here is that a reference to NewMail is copied into a temporary variable, temp, which refers to the chain of delegates at the moment the assignment is performed. Now, this method compares temp and null and invokes temp, so it doesn’t matter if another thread changes NewMail after the assignment to temp. Remember that delegates are immutable and this is why this technique works in theory. However, what a lot of developers don’t realize is that this code could be optimized by the compiler to remove the local temp variable entirely. If this happens, this version of the code is identical to the first version, so a NullReferenceException is still possible.

To really fix this code, you should rewrite OnNewMail like the following.

 

// Version 3

protected virtual void OnNewMail(NewMailEventArgs e) { EventHandler<NewMailEventArgs> temp = Volatile.Read(ref NewMail); if (temp != null) temp(this, e);

}

 

The call to Volatile.Read forces NewMail to be read at the point of the call and the refer- ence really has to be copied to the temp variable now. Then, temp will be invoked only if it is not null. See Chapter 29, “Primitive Thread Synchronization Constructs,” for more information about the Volatile.Read method.




 

As a convenience, you could define an extension method (as discussed in Chapter 8, “Methods”) that encapsulates this thread-safety logic. Define the extension method as follows.

 

public static class EventArgExtensions {

public static void Raise<TEventArgs>(this TEventArgs e,

Object sender, ref EventHandler<TEventArgs> eventDelegate) {

 

// Copy a reference to the delegate field now into a temporary field for thread safety EventHandler<TEventArgs> temp = Volatile.Read(ref eventDelegate);

 

// If any methods registered interest with our event, notify them if (temp != null) temp(sender, e);

}

}

 

And now, we can rewrite the OnNewMail method as follows.

 

protected virtual void OnNewMail(NewMailEventArgs e) { e.Raise(this, ref m_NewMail);

}

 

A class that uses MailManager as a base type is free to override the OnNewMail method. This capability gives the derived class control over the raising of the event. The derived class can handle the new email message in any way it sees fit. Usually, a derived type calls the base type’s OnNewMail method so that the registered method(s) receive the notification. However, the derived class might decide to disallow the event from being forwarded.

 

 

 
 

1 This was actually told to me by a member of the Microsoft JIT compiler team.


Step #4: Define a method that translates the input into the

Desired event

Your class must have some method that takes some input and translates it into the raising of the event. In my MailManager example, the SimulateNewMail method is called to indicate that a new email message has arrived into MailManager.

 

internal class MailManager {

 

// Step #4: Define a method that translates the

// input into the desired event

public void SimulateNewMail(String from, String to, String subject) {

 

// Construct an object to hold the information we want

// to pass to the receivers of our notification NewMailEventArgs e = new NewMailEventArgs(from, to, subject);

 

// Call our virtual method notifying our object that the event

// occurred. If no type overrides this method, our object will

// notify all the objects that registered interest in the event OnNewMail(e);

}

}

 

SimulateNewMail accepts information about the message and constructs a NewMailEventArgs object, passing the message information to its constructor. MailManager’s own virtual OnNewMail method is then called to formally notify the MailManager object of the new email message. Usually, this causes the event to be raised, notifying all of the registered methods. (As mentioned before, a class using MailManager as a base class can override this behavior.)

 

 


Date: 2016-03-03; view: 707


<== previous page | next page ==>
Nbsp;   Designing a Type That Exposes an Event | Nbsp;   How the Compiler Implements an Event
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.007 sec.)