SoFunction
Updated on 2025-03-07

Using dictionary to store event examples and implement custom event accessors in C#

Use a dictionary to store event instances
One usage of accessor-declarations is to expose many events but not assign fields to each event, but to use a dictionary to store these event instances. This is only useful if there are many events but you expect most events will not be implemented.

public delegate void EventHandler1(int i);
public delegate void EventHandler2(string s);

public class PropertyEventsSample
{
  private <string, > eventTable;

  public PropertyEventsSample()
  {
    eventTable = new <string, >();
    ("Event1", null);
    ("Event2", null);
  }

  public event EventHandler1 Event1
  {
    add
    {
      lock (eventTable)
      {
        eventTable["Event1"] = (EventHandler1)eventTable["Event1"] + value;
      }
    }
    remove
    {
      lock (eventTable)
      {
        eventTable["Event1"] = (EventHandler1)eventTable["Event1"] - value;
      }
    }
  }

  public event EventHandler2 Event2
  {
    add
    {
      lock (eventTable)
      {
        eventTable["Event2"] = (EventHandler2)eventTable["Event2"] + value;
      }
    }
    remove
    {
      lock (eventTable)
      {
        eventTable["Event2"] = (EventHandler2)eventTable["Event2"] - value;
      }
    }
  }

  internal void RaiseEvent1(int i)
  {
    EventHandler1 handler1;
    if (null != (handler1 = (EventHandler1)eventTable["Event1"]))
    {
      handler1(i);
    }
  }

  internal void RaiseEvent2(string s)
  {
    EventHandler2 handler2;
    if (null != (handler2 = (EventHandler2)eventTable["Event2"]))
    {
      handler2(s);
    }
  }
}

public class TestClass
{
  public static void Delegate1Method(int i)
  {
    (i);
  }

  public static void Delegate2Method(string s)
  {
    (s);
  }

  static void Main()
  {
    PropertyEventsSample p = new PropertyEventsSample();

    p.Event1 += new EventHandler1(TestClass.Delegate1Method);
    p.Event1 += new EventHandler1(TestClass.Delegate1Method);
    p.Event1 -= new EventHandler1(TestClass.Delegate1Method);
    p.RaiseEvent1(2);

    p.Event2 += new EventHandler2(TestClass.Delegate2Method);
    p.Event2 += new EventHandler2(TestClass.Delegate2Method);
    p.Event2 -= new EventHandler2(TestClass.Delegate2Method);
    p.RaiseEvent2("TestString");

    // Keep the console window open in debug mode.
    ("Press any key to exit.");
    ();
  }
}

Output:

  2
  TestString


Implement custom event accessors
An event is a special type of multi-channel delegate that can only be called from the class that declares it. Client code subscribes to events by providing references to the method called when the event is raised. These methods are added to the delegated call list through event accessors, which are similar to property accessors, except that event accessors are named add and remove. In most cases, there is no need to provide a custom event accessor. If you do not provide a custom event accessor in your code, the compiler will automatically add the event accessor. But in some cases, you may need to provide custom behavior. The following example demonstrates how to implement a custom add and remove event accessor. While any code inside these accessors can be replaced, it is recommended that you lock the event before adding or removing a new event handler method.

event EventHandler 
    {
      add
      {
        lock (PreDrawEvent)
        {
          PreDrawEvent += value;
        }
      }
      remove
      {
        lock (PreDrawEvent)
        {
          PreDrawEvent -= value;
        }
      }
    }