Sunday, July 26, 2009

Follow Up To “Creating and Raising Events – The Right Way”

Some time ago I made a post about common misunderstandings newcomers to C# have around declaring and raise events. There were two critical items I left out of or have learned since that post;

Race Condition In Standard Pattern

The standard pattern for raising an event that is well published every where looks like this;

    public event EventHandler MyEvent;
    public virtual void OnMyEvent()
    {
      if (MyEvent != null)
        MyEvent(this, new EventArgs());
    }


This is wrong however. While it appears to work, there is a race condition in this code; if a thread switch occurs after the if condition is evaluated and before the event is raised, then all the event handlers could be disconnected which would then lead to a System.NullReferenceException when the code actually tries to raise the event.



To get around this, you must cache the event reference in a local variable and then use the local variable in the code, like this;



    public event EventHandler MyEvent;
    public virtual void OnMyEvent()
    {
      EventHandler eventToRaise = MyEvent;
      if (eventToRaise != null)
        eventToRaise(this, new EventArgs());
    }


By caching a reference to the event before testing or raising it we can guarantee the value won’t change in the case of a thread switch, and therefore we avoid the null reference exception.



Declare Event Argument Properties as Read Only





When creating your own event argument objects it is good practice to ensure any properties you don’t expect to change during the event as read only. The only properties you shouldn’t make read only are ones you expect the event handlers to modify (return values), such as a ‘cancel’ parameter that you check after the event to determine if you should continue with your next operation.



There are two parts to declaring your properties read only. First, if the type of the property is a primitive or immutable type like int or string you should declare the field readonly and allow it to be passed in on the constructor;



  public class MyEventArgs : EventArgs
  {
    private readonly int m_Value;
    public MyEventArgs(int value)
    {
      m_Value = value;
    }
  }


The readonly keyword tells the C# compiler that field can only be set in the constructor of the class, trying to set it anywhere else will result in a compilation error. Note, you shouldn’t do this with non-immutable or primitive types, like other objects as you’ll get compile warnings or code analysis errors because event though the reference can’t be changed the properties on the referenced object can and therefore technically the property value can still change.



The second thing to do, which applies to a read only property of any type, is to ensure the property itself has no setter;



  public class MyEventArgs : EventArgs
  {
    private readonly int m_Value;
    public MyEventArgs(int value)
    {
      m_Value = value;
    }
    public int Value
    {
      get { return m_Value; }
    }
  }


Now you can be sure that no event handler can change the value of your event arguments and cause subsequent event handlers to behave unexpectedly.







Technorati Tags: ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,





Windows Live Tags: Events,Some,items,Race,Condition,Standard,Pattern,event,EventHandler,MyEvent,OnMyEvent,EventArgs,code,System,NullReferenceException,cache,reference,exception,custom,arguments,MyEventArgs,parameter,integer,needs,Argument,Read,practice,operation,result,compilation,error,Note,objects,analysis,Value,handler,newcomers,handlers,errors,eventToRaise,constructor,m_Value







WordPress Tags: Events,Some,items,Race,Condition,Standard,Pattern,event,EventHandler,MyEvent,OnMyEvent,EventArgs,code,System,NullReferenceException,cache,reference,exception,custom,arguments,MyEventArgs,parameter,integer,needs,Argument,Read,practice,operation,result,compilation,error,Note,objects,analysis,Value,handler,newcomers,handlers,errors,eventToRaise,constructor,m_Value







Blogger Labels: Events,Some,items,Race,Condition,Standard,Pattern,event,EventHandler,MyEvent,OnMyEvent,EventArgs,code,System,NullReferenceException,cache,reference,exception,custom,arguments,MyEventArgs,parameter,integer,needs,Argument,Read,practice,operation,result,compilation,error,Note,objects,analysis,Value,handler,newcomers,handlers,errors,eventToRaise,constructor,m_Value

No comments:

Post a Comment