CSharp/C# Tutorial - C# Events






Event system is central part of a software system. For example, button can fire clicked event. Batch job can generate job complete events. Student system would use registration events.

In event system, there would be two roles: broadcaster and subscriber.

The broadcaster is a type that contains a delegate field.

The broadcaster decides when to broadcast, by invoking the delegate.

The subscribers are the method target recipients.

A subscriber decides when to start and stop listening, by calling += and -= on the broadcaster's delegate.

Events are a C# language feature that formalizes this pattern.

Example

To declare an event, put the event keyword in front of a delegate member:

// a delegate
public delegate void ValueChangedHandler (decimal oldPrice, decimal newPrice);

public class Broadcaster {
   // Event declaration
   public event ValueChangedHandler PriceChanged;
}

Code within the Broadcaster type has full access to PriceChanged and can treat it as a delegate.

Code outside of Broadcaster can only perform += and -= operations on the PriceChanged event.





Standard Event Pattern

The .NET Framework defines a standard pattern for writing events.

System.EventArgs is a predefined Framework class with no members.

EventArgs is a base class for carrying information for an event.

The following code subclasses EventArgs to store the old and new prices when a PriceChanged event is fired:

public class PriceChangedEventArgs : System.EventArgs {
    public readonly decimal LastPrice;
    public readonly decimal NewPrice;
    public PriceChangedEventArgs (decimal lastPrice, decimal newPrice) {
        LastPrice = lastPrice;
        NewPrice = newPrice;
    }
}

The EventArgs subclass is named according to the information it contains.

It typically exposes data as properties or as read-only fields.

After creating the EventArgs subclass, the next step is to define a delegate for the event which is the event handler delegate.

The event handler delegate must have a void return type.

The event handler delegate must accept two arguments: the first of type object, and the second a subclass of EventArgs.

The first argument indicates the event broadcaster, and the second argument contains the extra information to convey.

The event handler delegate's name must end with EventHandler.

The Framework defines a generic delegate called System.EventHandler<> that satisfies these rules:


public 
delegate 
void 
EventHandler<TEventArgs> (object source, TEventArgs e) 
where TEventArgs : EventArgs;

The following code defines the delegate for our value changed event.

public delegate void ValueChangedHandler (object sender, PriceChangedEventArgs e);

The next step is to define an event of the chosen delegate type.

Here, we use the generic EventHandler delegate:

public class Product {
    public event EventHandler<PriceChangedEventArgs> PriceChanged;
}

Finally, the pattern requires that you write a protected virtual method that fires the event.

The name must match the name of the event, prefixed with the word On and accept a single EventArgs argument:

public class Product {
    ...
    public event EventHandler<PriceChangedEventArgs> PriceChanged;
    protected virtual void OnPriceChanged (PriceChangedEventArgs e) {
        if (PriceChanged != null) PriceChanged (this, e);
    }
}




Example 2

Here's the complete example:


using System;/*www  .  j  a va  2s .  com*/

public class PriceChangedEventArgs : EventArgs {
    public readonly decimal LastPrice;
    public readonly decimal NewPrice;

    public PriceChangedEventArgs (decimal lastPrice, decimal newPrice) {
        LastPrice = lastPrice; NewPrice = newPrice;
    }
}

public class Product {
    string symbol;
    decimal price;

    public Product (string symbol) {
       this.symbol = symbol;
    }
    
    public event EventHandler<PriceChangedEventArgs> PriceChanged;
    
    protected virtual void OnPriceChanged (PriceChangedEventArgs e) {
        if (PriceChanged != null) {
           PriceChanged (this, e);
        }
    }
    public decimal Price {
       get { 
          return price; 
       }
       set {
         if (price == value) return;
         decimal oldPrice = price;
         price = value;
         OnPriceChanged (new PriceChangedEventArgs (oldPrice, price));
      }
    }
}
class Test {
     static void Main()  {
        Product stock = new Product ("THPW");
        stock.Price = 2.10M;
        // Register with the PriceChanged event
        stock.PriceChanged += stock_PriceChanged;
        stock.Price = 3.9M;
     }
     static void stock_PriceChanged (object sender, PriceChangedEventArgs e) {
         if (e.NewPrice - e.LastPrice > 0){
             Console.WriteLine ("Alert!");
         }
     }
}

Event Accessors and Modifiers

An event's accessors are the implementations of its += and -= functions.

By default, accessors are implemented implicitly by the compiler.

Like methods, events can be virtual, overridden, abstract, or sealed.

Events can also be static:


public class Product {
    public static event EventHandler<EventArgs> StaticEvent;
    public virtual event EventHandler<EventArgs> VirtualEvent;
}