1. Multicast delegate: The delegate that is declared with the Delegate keyword inherits the delegate and MulticastDelegate types by default after compilation, so the declared delegate naturally contains the properties of the multicast delegate. That is, a delegate variable can call a method chain (multiple methods with the same signature). In C #, the implementation of a multicast delegate is a common pattern, designed to avoid a lot of manual coding, which is called observer (Observer) or Publish-subscribe (publish-subscribe) It's going to deal with a situation like this, You need to broadcast a single event notification (such as a change in the state of an object) to multiple subscribers.
2. applying the "-=" operator to a delegate returns a new instance: Since the delegate is a reference type, it is certain that someone will find it strange, why assign to a local variable, and then use that local variable to guarantee the thread safety of the null check? Since Localonchange points to a position where ontemperaturechange points, it is natural to conclude that any changes that occur in Ontemperaturechange will be reflected in the localonchange. But is that the truth?
The answer is no, because in fact any call to ontemperaturechange-= <listener> does not delete a delegate from Ontemperaturechange and makes its delegate less than the previous one. Instead, a completely new multicast delegate is assigned to it, which does not have any effect on the original multicast delegate (Localonchange also points to the original multicast delegate). You can view the code in the thermostat class.
3. delegate Operator: "+ =" and "-=" operation, representing the addition and removal of a delegate method, using the "=" assignment operator will clear all subscribers, of course, the use of "+" and "-" operation effect is the same. Of course, the operators, whether "+ = = +-", are implemented internally using static methods Delegate.combine () and Delegate.remove (). The Combine () method supports two parameters that are null.
4. The order invocation of the delegate: in a delegate variable containing multiple delegate methods (subscribers), which executes a delegate variable (publish), not the concurrent execution of multiple delegate methods, but the sequential execution of the contained delegate method, usually in the order of the addition of the execution of the method in the chain of delegates, However, this sequence is likely to be overwritten, so programmers should not rely on a particular invocation order.
5. the internal mechanism of the multicast delegate: the declaration delegate that previously explained the delegate keyword inherits the delegate and MulticastDelegate types, This means that the delegate keyword is an alias of a type derived from MulticastDelegate. The MulticastDelegate class, in addition to containing an object reference (target) and a method pointer, is the same as its delegate base class, and contains a reference to another MulticastDelegate object. When you add a method to a multicast delegate, the MulticastDelegate class creates a new instance of the delegate type, stores the object reference and method pointers in the new instance for the newly added method, and adds a new delegate instance as the next item in the delegate instance list, which results in The MulticastDelegate class maintains a linked list of multiple delegate objects (which can be understood as having a similar collection function within a multicast delegate to manage multiple delegate methods).
6. error handling for multicast delegates: Error handling highlights the importance of sequential notifications, and if a subscriber throws an exception, subsequent subscribers in the chain receive no notification. To avoid this problem, so that all Subscribers are notified, you must manually traverse the list of subscribers 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: in a multicast delegate to get the return value of each subscriber, you need to manually traverse the delegate chain to receive the return value, otherwise the direct invocation of the delegate chain returns the return value of the last delegate method, as well as the same ref, out-decorated parameters.
8. The difference between the event and the delegate: in the thermostat class, you can not use the event keyword to decorate the delegate variable, directly using the delegate variable processing can also do the corresponding work, but directly using the delegate, there are 2 hidden dangers, respectively, " Using the assignment operator overrides the previous delegate method, you can perform the publication outside of the declaring class, and you can encapsulate the delegate by using an event (the code that is generated by the compiler). The corresponding 2 hidden trouble is eliminated, it can only execute the publication in the Declaration class, can only register and eliminate the delegate (cannot overwrite, empty).
9.eventhandler<teventargs> The delegate is a generic delegate added by. net2.0 that contains the object type trigger source, the event tag parameter (which can inherit the extension of other data), and the use of the Sender-eventargs pattern in any class without declaring its own delegate definition, and you can use the Eventhandle generic delegate directly.
The internal mechanism of the event: the C # compiler gets the public delegate variable with the event modifier and declares the delegate private, in addition to 2 methods and 2 special event blocks. Essentially, the event keyword is a C # shortcut that the compiler uses to generate the appropriate encapsulation logic. You can view the code for the Thermostat2 class.
The implementation of custom events: the code generated by the compiler for "+ =" and "-=" can be customized, for example, assuming that the scope of the Ontemperaturechange delegate is changed to make it protected instead of private, This allows access from derived classes without the same restrictions as the external classes, which enables the addition of custom add and remove blocks to add their own implementations for each component of the event encapsulation. You can view the code for the THERMOSTAT3 class.
Public classthermostat{Private floatcurrenttemperature; Public floatCurrenttemperature {Get{returnCurrenttemperature;} Set { if(Currenttemperature! =value) {Currenttemperature=value; /*1. Here is not the beginning of the check null value, but instead of first assigning Ontemperaturechange to another delegate variable Localonchange, this simple modification can be ensured between checking null value and sending notification, if * all Onte Mperaturechange subscribers are removed (by a different thread), the NullReferenceException exception is not triggered. * 2. */ varLocalonchange =Ontemperaturechange; if(Localonchange = =NULL) { return; } //the traversal and error handling here prevents the subsequent execution of the delegate method from being interrupted when there is an error in a delegate method in the delegate chain when the multicast delegate executes. foreach(Eventhandler<temperatureargs> Iteminchlocalonchange.getinvocationlist ()) { Try{Item ( This,NewTemperatureargs (value)); } Catch(Exception ex) {Console.WriteLine (ex). Message); } } } } } //Ontemperaturechange = delegate {}; Assigns an empty delegate that represents a collection of 0 listeners that can be raised to listen without having to check for any listeners. Public EventEventhandler<temperatureargs>Ontemperaturechange; /// <summary> ///Observer Pattern Example/// </summary> Public Static voidObserver () {Adjust heater=NewAdjust ("Heater", 50f, (original, NewValue) = original >newvalue); Adjust Cooler=NewAdjust ("Cooler", 70f, (original, newvalue) = Original <newvalue); Thermostat Thermostat=Newthermostat (); Thermostat. Ontemperaturechange+=heater. Ontemperaturechange; Thermostat. Ontemperaturechange+ = (sender, Eargs) + = {Throw Newapplicationexception ();}; Thermostat. Ontemperaturechange+=Cooler. Ontemperaturechange; Thermostat. Currenttemperature=40f; Thermostat. Ontemperaturechange (NULL,NULL); }} Public classtemperatureargs:eventargs{ PublicTemperatureargs (floatnewtemprature) {Newtemprature=newtemprature; } Public floatnewtemprature {Get;Set; }}//once the C # compiler encounters the event keyword, it generates CIL code that is equivalent to the code in the Thermostat2 class. Public classthermostat2{/*private eventhandler<temperatureargs> Ontemperaturechange; public void Add_ontemperaturechange (eventhandler<temperatureargs> handler) {Delegate.combine (Ontemperatur Echange, Handler); } public void Remove_ontemperaturechange (eventhandler<temperatureargs> handler) {Delegate.remove (onTem Peraturechange, Handler); } public Event eventhandler<temperatureargs> Ontemperaturechange {add {add_ontemperaturechange (value) ; } Remove {Remove_ontemperaturechange (value);} }*/}//implementation of custom events Public classthermostat3{protectedEventhandler<temperatureargs>Ontemperaturechange; Public EventEventhandler<temperatureargs>Ontemperaturechange {add {//The last registered subscriber, notified by the first notificationdelegate.combine (value, Ontemperaturechange); } remove {Delegate.remove (ontemperaturechange, value);} }}/// <summary>///regulator for control of heater and cooler switches/// </summary> Public classadjust{ PublicAdjust (stringControllername,floatTemperature, func<float,float,BOOL>predicate) {Controllername=controllername; Temperature=temperature; Predicateswitch=predicate; } /// <summary> ///controller name, can be cooler, or heater/// </summary> Public stringcontrollername {Get;Set; } Public floattemperature {Get;Set; } Publicfunc<float,float,BOOL> Predicateswitch {Get;Private Set; } Public voidOntemperaturechange (Objectsender, Temperatureargs Eargs) {Console.WriteLine ("{0}: {1}", Controllername, Predicateswitch (temperature, eargs.newtemprature)?" on":"Off"); }}
View Code
C # Learning Note 9