11.Events

时间:2023-01-19 19:17:11

1.A type that defines an event member allows the type (or instances of the type) to notify other objects that something special has happened

2.defining an event member means that a type is offering the following capabilities:

  1.A method can register its interest in the event

  2.A method can unregister its interest in the event.

  3.Registered methods will be notified when the event occurs.

  because they maintain a list of the registered methods. When the event occurs, the type notifies all of the registered methods in the collection

3.The common language runtime’s (CLR’s) event model is based on delegates.

A delegate is a typesafe way to invoke a callback method.

Callback methods are the means by which objects receive the notifications they subscribed to.

示例:

11.Events

the application initializes by constructing an instance of MailManager.

MailManager offers a NewMail event. When the Fax and Pager

objects are constructed, they register an instance method with MailManager’s NewMail event so that
MailManager knows to notify the Fax and Pager objects when new email messages arrive.

Now,when MailManager receives a new email message (sometime in the future), it will raise the NewMail event, giving all of the registered methods an opportunity to process the new message in any way they want.

1.Designing a Type That Exposes an Event

steps:

  1.Define a type that will hold any additional information that should be sent to receivers of the event notification

  This additional information needs to be encapsulated into its own class, which typically contains a bunch of private fields along with some read-only public properties to expose these fields.

  By convention, classes that hold event information to be passed to the event handler should be derived from System.EventArgs, and the name of the class should be suffixed with EventArgs.

  11.Events

  2.Define the event member

  using the C# keyword event.

  Each event member is given accessibility(which is almost always public so that other code can access the event member),a type of delegate indicating the prototype of the method(s) that will be called

  a name (which can be any valid identifier)

  11.Events

  NewMail is the name of this event. The type of the event member is EventHandler<NewMailEventArgs>, which means that all receivers of the event notification must supply a callback method whose prototype matches that of the EventHandler<NewMailEventArgs> delegate type.

  Because the generic System.EventHandler delegate is defined as follows.

  public delegate void EventHandler<TEventArgs>(Object sender, TEventArgs e);

  the method prototypes must look like the following.
  void MethodName(Object sender, NewMailEventArgs e);

  3.Define a method responsible for raising the event to notify registered objects that the event has occurred

  By convention, the class should define a protected

    virtual method that is called by code internally within the class and its derived classes when the event is to be raised.

  This method takes one parameter,a NewMailEventArgs object, which includes the information passed to the objects receiving the notification.

  11.Events

  优化:define an extension method that encapsulates this thread-safety logic

  11.Events11.Events

  A class that uses MailManager as a base type is free to override the OnNewMail method.

  This capability gives the derived class control over the raising of the event. The derived class can handle

the new email message in any way it sees fit.

  Usually, a derived type calls the base type’s OnNewMail method so that the registered method(s) receive the notification. However, the derived class might decide to disallow the event from being forwarded.

  4.Define a method that translates the input into the desired event

  11.Events

其他知识点:

  1.The EventArgs class is defined in the Microsoft .NET Framework Class Library (FCL)

  11.Events

  this type is nothing to write home about. It simply serves as a base type from which other types can derive.

  Many events don’t have any additional information to pass on.just use EventArgs.Empty rather than constructing a new EventArgs object.

  2.why the event pattern requires the sender parameter to always be of type Object?

  (1) the cast is required, so the sender parameter might as well be typed as Object.like

  void MethodName(MailManager sender, NewMailEventArgs e);

  (2)typing the sender parameter as Object is just flexibility.

  It allows the delegate to be used by multiple types that offer an event that passes a NewMailEventArgs object.

  this is to add additional consistency to the pattern, making it easier for developers to learn and implement the pattern

  For example, a PopMailManager class could use the delegate even if this class were not derived from MailManager.The event pattern also requires that the delegate definition and the callback method name the EventArgs-derived parameter e.

  Tools that spit out source code (such as Microsoft Visual Studio) also know to call the parameter e.

  (3)the event pattern requires all event handlers to have a return type of void.

  This is necessary because raising an event might call several callback methods, and there is no way to get the return values from all of them.

  Having a return type of void doesn’t allow the callbacks to return a value.

  Unfortunately, there are some event handlers in the FCL, such as ResolveEventHandler, that did not follow Microsoft’s own prescribed pattern because it returns an object of type Assembly.

  (4)Raising an Event in a Thread-Safe Way

  11.Events

  The problem with the OnNewMail method is that the thread could see that NewMail is not null, and then, just before invoking NewMail, another thread could remove a delegate from the chain making NewMail null, resulting in a NullReferenceException being thrown

  优化:理论正确,但编译器优化之后还是错误的

  11.Events

  it doesn’t matter if another threadchanges NewMail after the assignment to temp. but,Remember that delegates are immutable and this is why this technique works in theory.

  However, what a lot of developers don’t realize is that this code could be optimized by the compiler to remove the local temp variable entirely.

  If this happens, this version of the code is identical to the first version, so a NullReferenceException  is still possible.

  优化:真正正确

  11.Events

  Volatile.Read forces NewMail to be read at the point of the call and the reference really has to be copied to the temp variable now.

  Then, temp will be invoked only if it is not null.

  the just-in-time (JIT) compiler is aware of this pattern and it knows not to optimize away the local temp variable.

  Specifically, all of Microsoft’s JIT compilers respect the invariant of not introducing new reads to heap memory and therefore, caching a reference in a local variable ensures that the heap reference is accessed only once.

  events are mostly used in singlethreaded scenarios (Windows Presentation Foundation and Windows Store apps) and so thread safety is not an issue anyway.

  It is very important to note that due to this thread race condition, it is also possible that a method will be invoked after it has been removed from the event’s delegate chain.

2.How the Compiler Implements an Event

  public event EventHandler<NewMailEventArgs> NewMail;

  compiling

  11.Events

  11.Events

  1.The first construct is simply a field of the appropriate delegate type. This field is a reference to the head of a list of delegates that will be notified when this event occurs.

  This field is initialized to null, meaning that no listeners have registered interest in the event

  field, NewMail in this example, is always private even though the original line of source code defines the event as public.to prevent code outside the defining class from manipulating it improperly

  2.If you attempt to remove a method that was never added, then Delegate’s Remove method internally does nothing. That is, you get no exception or warning of any type; the event’s collection of methods remains unchanged.

  3.The add and remove methods use a well-known pattern to update a value in a thread-safe way

  4.when you define an event in a type, the accessibility of the event determines what code can register and unregister interest in the event, but only the type itself can ever access the delegate field directly

  6.compilers also emit an event definition entry into the managed assembly’s metadata.

  This entry contains some flags and the underlying delegate type, and refers to the add and remove accessor methods.

  This information exists simply to draw an association between the abstract concept of an “event” and its accessor methods.

  Compilers and other tools can use this metadata

  and this information can also be obtained by using the System.Reflection.EventInfo class.

  However, the CLR itself doesn’t use this metadata information and requires only the accessor methods at run time.

3.Designing a Type That Listens for an Event

how to define a type that uses an event provided by another type

11.Events

11.Events

1. When the email application initializes, it would first construct a MailManager object and save the reference to this object in a variable. Then the application would construct a Fax object, passing the reference to the MailManager object as a parameter

2.mm.NewMail += FaxMsg;

compiling

mm.add_NewMail(new EventHandler<NewMailEventArgs>(this.FaxMsg));

the C# compiler is generating code that will construct an EventHandler<NewMailEventArgs> delegate object that wraps the Fax class’s FaxMsg method

3.As long as an object has registered one of its methods with an event, the object can’t be garbage collected.

If your type implements IDisposable’s Dispose method, the implementation should cause it to unregister interest in all events

4.C# requires your code to use the += and -= operators to add and remove delegates from the list.

If you try to call the add or remove method explicitly, the C# compiler produces the CS0571 cannot explicitly call operator or accessor error message.

4.Explicitly Implementing an Event

原因:

The System.Windows.Forms.Control type defines about 70 events. If the Control type implemented 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

the ASP.NET System.Web.UI.Control and the Windows Presentation Foundation (WPF) System.Windows.UIElement type also offer many events that most programmers do not use.

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.

1.To efficiently store event delegates, each object that exposes events will maintain a collection (usually a dictionary) with some sort of event identifier as the key and a delegate list as the value. collection store events that developer interested

  (1).When the object needs to raise an event, the event identifier is looked up in the collection.

  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.

  11.Events

  11.Events

2.show a class that uses my EventSet class. This class has a field that refers to an EventSet object, and each of this class’s events is explicitly implemented

  11.Events

  11.Events

  3.uses the TypeWithLotsOfEvents type can’t tell whether the events have been implemented implicitly by the compiler or explicitly by the developer.They just register the events by using normal syntax.

  11.Events

其他知识:

  1.The FCL defines a type, System.Windows.EventHandlersStore, which does essentially the same thing as my EventSet class.

  Various WPF types use the EventHandlersStore type internally to maintain their sparse set of events. You’re certainly welcome to use the FCL’s EventHandlersStore type if you’d like.

  The big difference between the EventHandlersStore type and my EventSet type is that EventHandlersStore doesn’t offer any thread-safe way to access the events;

  you would have to implement your own thread-safe wrapper around the EventHandlersStore collection if you need to do this.

相关文章