Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Explicitly Implementing an Event

The System.Windows.Forms.Control type defines about 70 events. If the Control type imple- mented the events by allowing the compiler to implicitly generate the add and remove accessor methods and delegate fields, every Control object would have 70 delegate fields in it just for the events! Because most programmers care about just a few events, an enormous amount of memory would be wasted for each object created from a Control-derived type. By the way, the ASP.NET System.Web.UI.Control and the Windows Presentation Foundation (WPF) System.Windows.UI­ Element type also offer many events that most programmers do not use.

In this section, I discuss how the C# compiler allows a class developer to explicitly implement an event, allowing the developer to control how the add and remove methods manipulate the callback delegates. I’m going to demonstrate how explicitly implementing an event can be used to efficiently implement a class that offers many events. However, there are certainly other scenarios where you might want to explicitly implement a type’s event.


To efficiently store event delegates, each object that exposes events will maintain a collection (usu- ally a dictionary) with some sort of event identifier as the key and a delegate list as the value. When a new object is constructed, this collection is empty. When interest in an event is registered, the event’s identifier is looked up in the collection. If the event identifier is there, the new delegate is combined with the list of delegates for this event. If the event identifier isn’t in the collection, the event identifier is added with the delegate.

When the object needs to raise an event, the event identifier is looked up in the collection. If the collection doesn’t have an entry for the event identifier, nothing has registered interest in the event and no delegates need to be called back. If the event identifier is in the collection, the delegate list associated with the event identifier is invoked. Implementing this design pattern is the responsibility of the developer who is designing the type that defines the events; the developer using the type has no idea how the events are implemented internally.

Here is an example of how you could accomplish this pattern. First, I implemented an EventSet

class that represents a collection of events and each event’s delegate list as follows.

 

using System;

using System.Collections.Generic;

 

// This class exists to provide a bit more type safety and

// code maintainability when using EventSet public sealed class EventKey { }

 

public sealed class EventSet {

// The private dictionary used to maintain EventKey ­> Delegate mappings private readonly Dictionary<EventKey, Delegate> m_events =

new Dictionary<EventKey, Delegate>();

 

// Adds an EventKey ­> Delegate mapping if it doesn't exist or

// combines a delegate to an existing EventKey

public void Add(EventKey eventKey, Delegate handler) { Monitor.Enter(m_events);



Delegate d; m_events.TryGetValue(eventKey, out d);

m_events[eventKey] = Delegate.Combine(d, handler); Monitor.Exit(m_events);

}

 

// Removes a delegate from an EventKey (if it exists) and

// removes the EventKey ­> Delegate mapping the last delegate is removed public void Remove(EventKey eventKey, Delegate handler) {

Monitor.Enter(m_events);

// Call TryGetValue to ensure that an exception is not thrown if

// attempting to remove a delegate from an EventKey not in the set Delegate d;

if (m_events.TryGetValue(eventKey, out d)) { d = Delegate.Remove(d, handler);


// If a delegate remains, set the new head else remove the EventKey if (d != null) m_events[eventKey] = d;

else m_events.Remove(eventKey);

}

Monitor.Exit(m_events);

}

 

// Raises the event for the indicated EventKey

public void Raise(EventKey eventKey, Object sender, EventArgs e) {

// Don't throw an exception if the EventKey is not in the set Delegate d;

Monitor.Enter(m_events); m_events.TryGetValue(eventKey, out d); Monitor.Exit(m_events);

 

if (d != null) {

// Because the dictionary can contain several different delegate types,

// it is impossible to construct a type­safe call to the delegate at

// compile time. So, I call the System.Delegate type's DynamicInvoke

// method, passing it the callback method's parameters as an array of

// objects. Internally, DynamicInvoke will check the type safety of the

// parameters with the callback method being called and call the method.

// If there is a type mismatch, then DynamicInvoke will throw an exception. d.DynamicInvoke(new Object[] { sender, e });

}

}

       
   
 
 

 

Now, I show a class that uses my EventSet class. This class has a field that refers to an Event­ Set object, and each of this class’s events is explicitly implemented so that each event’s add method stores the specified callback delegate in the EventSet object and each event’s remove method elimi- nates the specified callback delegate (if found).

 

using System;

 

// Define the EventArgs­derived type for this event. public class FooEventArgs : EventArgs { }

 

public class TypeWithLotsOfEvents {

 

// Define a private instance field that references a collection.

// The collection manages a set of Event/Delegate pairs.

// NOTE: The EventSet type is not part of the FCL, it is my own type. private readonly EventSet m_eventSet = new EventSet();


// The protected property allows derived types access to the collection. protected EventSet EventSet { get { return m_eventSet; } }

 

#region Code to support the Foo event (repeat this pattern for additional events)

// Define the members necessary for the Foo event.

// 2a. Construct a static, read­only object to identify this event.

// Each object has its own hash code for looking up this

// event's delegate linked list in the object's collection. protected static readonly EventKey s_fooEventKey = new EventKey();

 

// 2b. Define the event's accessor methods that add/remove the

// delegate from the collection.

public event EventHandler<FooEventArgs> Foo {

add { m_eventSet.Add(s_fooEventKey, value); } remove { m_eventSet.Remove(s_fooEventKey, value); }

 

}

 

// 2c. Define the protected, virtual On method for this event. protected virtual void OnFoo(FooEventArgs e) {

m_eventSet.Raise(s_fooEventKey, this, e);

}

 

// 2d. Define the method that translates input to this event. public void SimulateFoo() { OnFoo(new FooEventArgs()); }

#endregion

}

 

Code that uses the TypeWithLotsOfEvents type can’t tell whether the events have been imple- mented implicitly by the compiler or explicitly by the developer. They just register the events by using normal syntax. Here is some code demonstrating this.

 

public sealed class Program { public static void Main() {

TypeWithLotsOfEvents twle = new TypeWithLotsOfEvents();

 

// Add a callback here twle.Foo += HandleFooEvent;

 

// Prove that it worked twle.SimulateFoo();

}

 

private static void HandleFooEvent(object sender, FooEventArgs e) { Console.WriteLine("Handling Foo Event here...");

}

}


 


C HA P T E R 1 2

Generics

In this chapter:

Generics in the Framework Class Library................................................. 270

Generics Infrastructure........................................................................... 271

Generic Interfaces.................................................................................. 277

Generic Delegates.................................................................................. 278


Date: 2016-03-03; view: 673


<== previous page | next page ==>
Nbsp;   Designing a Type That Listens for an Event | Delegate and Interface Contra-variant and
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.009 sec.)