Events and Delegates
A delegate is an object representing a function pointer. It is defined with a method signature (the return type and argument).
public delegate string MyDelegate(bool isOK);
- Note multicast delegates (such as events) must return void.
- The arguments by convention are Object sender and a derived class of EventArgs.
An event tells subscribers that they must use a method that looks like the delegate.
public event MyDelegate MyEvent;
In .Net 2 you can avoid creating a delegate with the generic EventHandler (msdn)
public event EventHandler<MyEventArgs> MyEvent;
Declare Event
A generic EventArgs<T> : EventArgs is handy for passing strong typed data (strings, int, object)
public class Worker
{
public event EventHandler<GenericEventArgs<string>> WorkDone;
public void Execute(string m)
{
EventHandler<GenericEventArgs<string>> handler = WorkDone;
if (handler != null) handler(this, new GenericEventArgs<string>(m));
}
}
public class GenericEventArgs<T> : EventArgs
{
public GenericEventArgs(T data)
{
Data = data;
}
public T Data { get; set; }
}
Raise Event
To raise the event (publish it):
//cast the delegate as the event. If there is no subscriber, the event is null
MyDelegate handler = MyEvent;
if (handler != null) handler(this, args);
You must use the raise event pattern (see C# faq). NB: if you don't assign the copy, it's not thread safe (there's a race condition between the check and the useage).
if (MyEvent != null) MyEvent(this, EventArgs.Empty); //compiles but not thread safe!!
In C# 6.0 the ?. null conditional operator is thread-safe.
MyEvent?.Invoke(this, EventArgs.Empty);
Consume Event
To consume it from a subscriber:
WorkClass wc = new WorkClass(); //scope needs to be class level
wc.MyEvent += new WorkClass.MyDelegate(MySubscriberMethod); //the new EventHandler syntax is optional...
wc.MyEvent += MySubscriberMethod; //works too!
You can inline it and use local variables:
decimal totalWeight; // Local
wc.FoundWeight +=
(delegate(object sender, GenericEventArgs<decimal> e) { totalWeight += e.Data; });
//or v3 lamdba style
shipper.RecordCTotalWeight += ((sender, e) => totalWeight += e.Data);