Friday, August 17, 2007

Why do you want to explicit declare an event.

In C#, you can simply declare an event by using this line :

public event EventHandler<EventArgs> MyImplicitEvent;
It's simple. But under the hood, the compiler did a lot of things for you. The line above will be translated into the

   1: // 1. A PRIVATE delegate field that is initialized to nullprivate 
   2: EventHandler<EventArgs> MyImplicitEvent = null;
   3: // 2. A PUBLIC add_Xxx method (where xxx is the Event name)
   4: // Allows objects to register interest in the event.
   5: [MethodImpl(MethodImplOptions.Synchronized)]
   6: public void add_MyImplicitEvent(EventHandler<EventArgs> value) 
   7: {
   8:     MyImplicitEvent = (EventHandler<EventArgs>)
   9:     Delegate.Combine(MyImplicitEvent, value);
  10: }
  11: // 3. A PUBLIC remove_Xxx method (where Xxx is the Event name)
  12: // Allows objects to unregister interest in the event.
  13: [MethodImpl(MethodImplOptions.Synchronized)]
  14: public void remove_MyImplicitEvent(EventHandler<EventArgs> value) 
  15: {
  16:     MyImplicitEvent = (EventHandler<EventArgs>)Delegate.Remove(MyImplicitEvent, value);
  17: }


So, the compiler will inject the MethodImplOptions.Synchronized attribute into the add_ and remove_ method. By default, those methods implement the synchronization using the object itself as the the thread-synchronization lock. However, this object is public, and you shouldn't use this object to do the synchronization. Because the malicious code could use this public object to deadlock threads that use this instance of object.


A better way to handle this to do the parts which compiler generated for you by yourself. like this.





   1: // Private instance field created to serve as thread synchronization lock       
   2:  private readonly object m_eventLock = new Object();        
   3:  // Add private field that refers to the head of the delegate list       
   4:  private EventHandler<EventArgs> _myImplicitEvent;       
   5:  // Add an event member to the class        
   6:  public event EventHandler<EventArgs> ImplicitEvent        
   7:  {            // Explicitly implement the 'add' method            
   8:      add            
   9:      {                
  10:          // Take the private lock and add a handler                
  11:          // (passed as 'value') to the delegate list                
  12:          lock (m_eventLock) { _myImplicitEvent += value; }            
  13:      }            
  14:      // Explicitly implement the 'remove' method            
  15:      remove           
  16:      {                
  17:          // Take the private lock and remove a handler                
  18:          // (passed as 'value') from the delegate list               
  19:          lock (m_eventLock) { _myImplicitEvent -= value; }            
  20:      }        
  21:  }

This way, you use the private object m_event Lock to do the synchronization, and you can avoid the problem (code outside of the class will have access to the synchronization object) compiler generated codes have.


No comments: