Events
When using delegates, two emergent roles commonly appear: 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. A subscriber does not know about, or interfere with, other subscribers.
Events are a language feature that formalizes this pattern. An event is a wrapper for a delegate that exposes just the subset of delegate features required for the broadcaster/subscriber model. The main purpose of events is to prevent subscribers from interfering with each other.
To declare an event member, you put the event keyword in front of a delegate member. For instance:
public class Broadcaster { publicevent ProgressReporter Progress; }
Code within the Broadcaster type has full access to Progress and can treat it as a delegate. Code outside of Broadcaster can only perform += and -= operations onProgress.Consider the following example. The Stock class invokes its PriceChanged event every time the Price of the Stock changes:public delegate void PriceChangedHandler (decimal oldPrice, decimal newPrice); public class Stock { string symbol; decimal price; public Stock (string symbol) {this.symbol = symbol;}public event PriceChangedHandler PriceChanged; public decimal Price { get { return price; } set { if (price == value) return; // exit if nothing has changed if (PriceChanged != null) // if invocation list not empty PriceChanged (price, value); // fire event price = value; } } }If we remove the event keyword from our example so that PriceChanged becomes an ordinary delegate field, our example would give the same results. However, Stockwould be less robust, in that subscribers could do the following things to interfere with each other:
Replace other subscribers by reassigning PriceChanged (instead of using the += operator). Clear all subscribers (by setting PriceChanged to null). Broadcast to other subscribers by invoking the delegateStandard Event Pattern
The .NET Framework defines a standard pattern for writing events. Its purpose is to provide consistency across both Framework and user code. At the core of the standard event pattern is System.EventArgs: a predefined Framework class with no members (other than the static Empty property). EventArgs is a base class for conveying information for an event. In our Stock example, we would subclass EventArgs to convey 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; } }With an EventArgs subclass in place, the next step is to choose or define a delegate for the event. There are three rules:
It must have a void return type. It 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. Its name must end in "EventHandler"The Framework defines a generic delegate called System.EventHandler<> that satisfies these rules:public delegate void EventHandler(object source, TEventArgs e) where TEventArgs : EventArgs; The next step is to define an event of the chosen delegate type. Here, we use the generic EventHandler delegate:public class Stock { ...public event EventHandlerPriceChanged; } 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 then accept a single EventArgs argument:public class Stock { ... public event EventHandlerPriceChanged;protected virtual void OnPriceChanged (PriceChangedEventArgs e) { if (PriceChanged != null) PriceChanged (this, e); } } This provides a central point from which subclasses can invoke or override the event.Here's the complete example:using System; public class PriceChangedEventArgs : EventArgs { public readonly decimal LastPrice; public readonly decimal NewPrice; public PriceChangedEventArgs (decimal lastPrice, decimal newPrice) { LastPrice = lastPrice; NewPrice = newPrice; } } public class Stock { string symbol; decimal price; public Stock (string symbol) {this.symbol = symbol;} public event EventHandlerPriceChanged;protected virtual void OnPriceChanged (PriceChangedEventArgs e) { if (PriceChanged != null) PriceChanged (this, e); } public decimal Price { get { return price; } set { if (price == value) return; OnPriceChanged (new PriceChangedEventArgs (price, value)); price = value; } } } class Test { static void Main( ) { Stock stock = new Stock ("THPW"); stock.Price = 27.10M; // register with the PriceChanged event stock.PriceChanged += stock_PriceChanged; stock.Price = 31.59M; } static void stock_PriceChanged (object sender, PriceChangedEventArgs e) { if ((e.NewPrice - e.LastPrice) / e.LastPrice > 0.1M) Console.WriteLine ("Alert, 10% stock price increase!"); } } The predefined nongeneric EventHandler delegate can be used when an event doesn't carry extra information. In this example, we rewrite Stock such that thePriceChanged event is fired after the price changes, and no information about the event is necessary, other than it happened. We also make use of the EventArgs.Emptyproperty, in order to avoid unnecessarily instantiating an instance of EventArgs.public class Stock { string symbol; decimal price; public Stock (string symbol) {this.symbol = symbol;} public eventEventHandler PriceChanged; protected virtual void OnPriceChanged (EventArgs e) { if (PriceChanged != null) PriceChanged (this, e); } public decimal Price { get { return price; } set { if (price == value) return; price = value; OnPriceChanged (EventArgs.Empty); } } }Event Accessors
An event's accessors are the implementations of its += and -= functions. By default, accessors are implemented implicitly by the compiler. Consider this event declaration:public event EventHandler PriceChanged;The compiler converts this to the following:
A private delegate field A public pair of event accessor functions, whose implementations forward the += and -= operations to the private delegate fieldYou can take over this process by defining explicit event accessors. Here's a manual implementation of the PriceChanged event from our previous example:private EventHandler _PriceChanged; // declare a private delegate public event EventHandler PriceChanged {add { _PriceChanged += value; } remove { _PriceChanged -= value; } }This example is functionally identical to C#'s default accessor implementation. The add and remove keywords after the event declaration instruct C# not to generate a default field and accessor logic.With explicit event accessors, you can apply more complex strategies to the storage and access of the underlying delegate. There are three scenarios where this is useful:
When the event accessors are merely relays for another class that is broadcasting the event When the class exposes a large number of events, where most of the time very few subscribers exist, such as a Windows control. In such cases, it is better to store the subscriber's delegate instances in a dictionary, since a dictionary will contain less storage overhead than dozens of null delegate field references When explicitly implementing an interface that declares an eventHere is an example that illustrates the last point:public interface IFoo { event EventHandler Ev; } class Foo : IFoo { private EventHandler ev; event EventHandler IFoo.Ev { add { ev += value; } remove { ev -= value; } } }Event Modifiers
Like methods, events can be virtual, overridden, abstract, and sealed. Events can also be static:public class Foo { public static event EventHandlerStaticEvent; public virtual event EventHandler VirtualEvent; } Home this will help you.reference taken from o'reilly book.
No comments:
Post a Comment