C # study notes 9,
1.Multicast delegate: The Delegate declared by the keyword with delegate. After compilation, the Delegate inherited the delegate and MulticastDelegate types by default. Therefore, the Delegate declared naturally contains the multicast delegate Feature, that is, a delegate variable can call a method chain (multiple methods with the same signature ). In C #, multicast delegation is implemented in a common mode to avoid a large amount of manual encoding. This mode is called Observer (Observer) or publish-subscribe (publish-subscribe) in this case, you need to broadcast notifications of a single event (such as a change in the object status) to multiple subscribers.
2.When the "-=" operator is applied to the delegate, a new instance is returned. Since the delegate is a reference type, some people may find it strange. Why do you assign a value to a local variable, which local variable can be used to ensure the thread security of the Null check? Because localOnChange points to the OnTemperatureChange point, it is natural that any changes made in OnTemperatureChange will be reflected in localOnChange. But is that true?
The answer is no, because in fact, no call to OnTemperatureChange-= <listener> will delete a delegate from OnTemperatureChange, so that it has one fewer delegate than before. Instead, a new multicast delegate is assigned to it, which does not affect the original multicast delegate (localOnChange also points to the original multicast delegate ). You can view the code in the Thermostat class.
3.Delegate operators: "+ =" and "-=" indicate adding and removing a delegate method. Using the "=" Assign operator, all subscribers are cleared, of course, the "+" and "-" operations are the same. Of course, all the operators "+ =-= +-" are implemented using static Delegate. Combine () and Delegate. Remove () internally. The Combine () method supports null parameters.
4.Ordered call of delegation: A delegate variable contains multiple delegate methods (subscriber). The delegate variable (publish) is executed once instead of multiple delegate methods contained in the same execution period, instead, it executes the delegate methods in sequence. Generally, the methods in the delegate chain are executed in the order of addition, but this order may be overwritten. Therefore, programmers should not rely on a specific call order.
5.Internal Mechanism of multicast delegation: the Declaration delegate that describes the Delegate keyword previously inherits the delegate and MulticastDelegate types, that is, the delegate keyword is an alias of the type derived from MulticastDelegate. In addition to an object reference (Target) and Method pointer, The MulticastDelegate class is the same as its Delegate base class. In addition, it also contains references to another MulticastDelegate object. When a method is added to a multicast delegate, the MulticastDelegate class creates a new instance of the delegate type and stores object references and method pointers for new methods in the new instance, add a new delegated instance to the delegated instance list as the next item. The result is, the MulticastDelegate class maintains a linked list composed of multiple Delegate objects (it can be understood that the multicast Delegate has a similar set function to manage multiple Delegate methods ).
6.Multicast delegate error handling: Error Handling highlights the importance of ordered notifications. If a subscriber raises an exception, subsequent subscriber in the chain will not receive the notification. To avoid this problem, all subscribers must manually traverse the subscriber list and call them separately to put them in the try-catch Block. You can view the code in the Thermostat class.
7.Method return value and parameter reference: to obtain the return value of each subscriber in a multicast delegate, you must manually traverse the delegate chain to receive the return value, otherwise, directly calling the delegate chain will return the return value of the last delegate method. Similarly, the parameters modified by ref and out are the same.
8.Differences between events and delegation: In the Thermostat class, you can directly process the delegate variables without modifying the event keyword, but directly use the delegate, there are two hidden risks: "The assignment operator overwrites the previous delegate method and can be executed and released outside the Declaration class ", with event, you can encapsulate the delegate (the encapsulated code is generated by the compiler ). The corresponding two risks are eliminated. They can only be published in the Declaration class, and can only be registered and eliminated for the delegate (not covered, cleared ).
9.The EventHandler <TEventArgs> delegate is. the generic delegate added by net2.0 contains the object-type trigger source and event tag parameters (which can inherit and extend other data). The sender-EventArgs mode is used in any class, without declaring its own delegate definition, you can directly use the EventHandle generic delegate.
10.Internal Mechanism of the event: C # the compiler will obtain the public Delegate variable with the event modifier and declare the delegate private. In addition, it also adds two methods and two special event blocks. Essentially, the event keyword is a C # shortcut used by the compiler to generate the appropriate encapsulation logic. You can view the Thermostat2 class code.
11.Implementation of custom events: the code generated by the compiler for "+ =" and "-=" can be customized. For example, assume that the OnTemperatureChange delegate scope is changed, make it protected instead of private, so that you can access it from the derived class without being subject to the same restrictions as external classes. Therefore, C # allows you to add customized add and remove blocks, add your own implementation for each component of event encapsulation. You can view the code of the thermostat3.
Public class Thermostat {private float currentTemperature; public float CurrentTemperature {get {return currentTemperature;} set {if (currentTemperature! = Value) {currentTemperature = value;/* 1. in this case, the null value is not checked at the beginning. Instead, the OnTemperatureChange value is assigned to another delegate variable localOnChange. This simple modification can ensure that the check between the null value and the notification sending, if * All OnTemperatureChange subscribers are removed (by a different thread), NullReferenceException is not triggered. * 2. */var localOnChange = OnTemperatureChange; if (localOnChange = null) {return;} // traversal and error handling here are used to prevent multicast delegation execution, if an error occurs in a delegate method in the delegate chain, the subsequent delegate method execution will be interrupted. Foreach (EventHandler <TemperatureArgs> item in localOnChange. getInvocationList () {try {item (this, new TemperatureArgs (value);} catch (Exception ex) {Console. writeLine (ex. message) ;}}}// OnTemperatureChange = delegate {}; assign an empty delegate to represent a set composed of zero listeners, you do not have to check whether there are any listeners. Public event EventHandler <TemperatureArgs> OnTemperatureChange; // <summary> // Observer mode example /// </summary> public static void Observer () {Adjust heater = new Adjust ("heater", 50f, (original, newValue) => original> newValue); Adjust cooler = new Adjust ("cooler", 70f, (original, newValue) => original <newValue); Thermostat thermostat = new Thermostat (); thermostat. onTemperatureChange + = heater. onTe MperatureChange; thermostat. onTemperatureChange + = (sender, eArgs) =>{ throw new ApplicationException () ;}; thermostat. onTemperatureChange + = cooler. onTemperatureChange; thermostat. currentTemperature = 40f; thermostat. onTemperatureChange (null, null) ;}} public class TemperatureArgs: EventArgs {public TemperatureArgs (float newTemprature) {NewTemprature = newTemprature;} public float NewTempratur E {get; set ;}}// C # once the compiler encounters the event keyword, it will generate the CE code equivalent to the Code in Thermostat2. Public class Thermostat2 {/* private EventHandler <TemperatureArgs> onTemperatureChange; public void add_OnTemperatureChange (EventHandler <TemperatureArgs> handler) {Delegate. combine (onTemperatureChange, handler);} public void remove_OnTemperatureChange (EventHandler <TemperatureArgs> handler) {Delegate. remove (onTemperatureChange, handler);} public event EventHandler <TemperatureArgs> OnTemperatureCh Ange {add {add_OnTemperatureChange (value) ;}remove {custom (value) ;}} * // public class Thermostat3 {protected EventHandler <TemperatureArgs> onTemperatureChange; public event EventHandler <TemperatureArgs> OnTemperatureChange {add {// The last registered subscriber, which is the first to notify Delegate during notification. combine (value, onTemperatureChange);} remove {Delegate. remove (onTemperatureChange, value) ;}}/// <su Mmary> // regulator, used to control the switch between the heater and the refrigerator /// </summary> public class Adjust {public Adjust (string controllerName, float temperature, Func <float, float, bool> predicate) {ControllerName = controllerName; Temperature = temperature; predicateSwitch = predicate;} // <summary> // controller name, it can be a refrigerator, or a heater // </summary> public string ControllerName {get; set;} public float Temperature {get; set;} public Func <float, Float, bool> predicateSwitch {get; private set;} public void OnTemperatureChange (object sender, TemperatureArgs eArgs) {Console. writeLine ("{0 }:{ 1}", ControllerName, predicateSwitch (Temperature, eArgs. newTemprature )? "On": "Off ");}}
View Code