1. Multicast delegate: Delegate and Multicast Delegate are inherited by default after compilation by delegate keyword declaration delegate delegate, so the declared delegate naturally contains the characteristics of multicast delegate, that is, a delegate variable can call a method chain (multiple methods with the same signature). In C#, the implementation of multicast delegation is a general pattern to avoid a lot of manual coding. This pattern is called Observer or publish-subscribe. It copes with such a situation that you need to broadcast notifications of a single event (such as a change in object status) to multiple subscribers.
2. Applying the "-=" operator to a delegate returns a new instance: Since the delegate is a reference type, some people will wonder why assigning a local variable and then using that local variable can guarantee thread security for Null checks. Since localOnChange points to the location where OnTemperature Change points, it is natural to conclude that any change that occurs in OnTemperature Change will be reflected in localOnChange. But is that really the case?
The answer is no, because virtually no call to OnTemperatureChange - = <listener> removes a delegate from OnTemperatureChange and makes it one less delegate than before. Instead, a new multicast delegate is assigned to it, which has no effect on the original multicast delegate (localOnChange also points to that original multicast delegate). See the code in the Thermostat class.
3. Delegate operators: "+=" and "-=" operations represent the addition and removal of a delegate method. The use of "=" assignment operators will clear all subscribers, of course, the use of "+" and "-" operations is the same. Of course, both the "+=-=+-" operators are implemented internally using static methods Delegate.Combine() and Delegate.Remove(). The Combine() method supports null for both parameters.
4. Sequential invocation of delegation: A delegation variable contains multiple delegation methods (subscribers). It executes a delegation variable (publishing). It does not include multiple delegation methods in the execution period at the same time, but sequentially executes the contained delegation methods. It usually executes the methods in the delegation chain in the order of adding, but this order may be overwritten, so programmers do not. It should depend on a particular invocation order.
5. Internal mechanism of multicast delegation: The declaration delegate of delegate keyword, which has been explained before, inherits Delegate and Multicast Delegate types, that is to say, delegate keyword is an alias of a type derived from Multicast Delegate. The MulticastDelegate class contains not only an object reference (Target) and a method pointer (Method), which is the same as its Delegate base class, but also a reference to another MulticastDelegate object. When adding a method to a multicast delegate, the MulticastDelegate class creates a new instance of the delegate type, stores object references and method pointers for the new method in the new instance, and adds a new delegate instance to the delegate instance list as the next item. As a result, the MulticastDelegate class maintains a linked list of multiple Delegate objects. It is understood that there is a similar set function within multicast delegates to manage multiple delegation methods.
6. Error handling of multicast delegates: Error handling highlights the importance of sequential notifications. If a subscriber causes an exception, subsequent subscribers in the chain will not receive notifications. To avoid this problem, all subscribers are notified that they must manually traverse the list of subscribers and call them individually and put them in try-catch blocks. See the code in the Thermostat class.
7. Method return value and parameter reference: To get the return value of each subscriber in multicast delegation, we need to manually traverse the delegation link to collect the return value. Otherwise, calling the delegation chain directly will return the return value of the last delegation method. Similarly, ref and out modification parameters are the same.
8. The difference between event and delegation: In Thermostat class, delegation variables can be modified without event keyword, and can be processed directly with delegation variables. However, there are two hidden dangers when delegation is used directly. They are "using assignment operators to override previous delegation methods, and can be published outside the declared class". Event can be used to handle delegation. Encapsulation (encapsulated code generated by the compiler). The corresponding two hidden dangers will be eliminated, which can only be issued in the declaration class, can only register and eliminate the delegation (can not be covered, empty).
9. EventHandler < TEventArgs > delegate is a generic delegate added to. net2.0. It contains object type trigger source, event markup parameter (which can inherit and extend other data). Using sender-EventArgs mode in any class, it is not necessary to declare its own delegate definition, but can use EventHandle generic delegate directly.
10. Internal mechanism of events: The C_ compiler takes public delegate variables with event modifiers and declares the delegate private. In addition, it adds two methods and two special event blocks. Essentially, the event keyword is a C # shortcut that the compiler uses to generate the appropriate encapsulation logic. See the code for the Thermostat2 class.
11. Implementation of custom events: Code generated by the compiler for "+=" and "-=" can be customized. For example, assuming that the scope of the OnTemperature Change delegation is changed to protect rather than private, access can be made from derived classes without the same restrictions as external classes. For this reason, C# allows custom add and remove blocks to be added to encapsulate events. Each component of the installation adds its own implementation. See the code for the Thermostat 3 class.
public class Thermostat { private float currentTemperature; public float CurrentTemperature { get { return currentTemperature; } set { if (currentTemperature != value) { currentTemperature = value; /* 1.Instead of checking null values at the beginning, OnTemperature Change is first assigned to another delegate variable, localOnChange. This simple modification ensures that between checking null values and sending notifications, if * All OnTemperatureChange subscribers are removed (by a different thread), and the NullReference Exception exception is not triggered. * 2. */ var localOnChange = OnTemperatureChange; if (localOnChange == null) { return; } //The traversal and error handling here is to prevent delegation method execution from interrupting if there is a delegation method error in the delegation chain during multicast delegation execution. foreach (EventHandler<TemperatureArgs> item in localOnChange.GetInvocationList()) { try { item(this, new TemperatureArgs(value)); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } } } //OnTemperatureChange = delegate { };Assigning an empty delegate to represent a set of zero listeners can trigger a listener without checking for any listeners. public event EventHandler<TemperatureArgs> OnTemperatureChange; /// <summary> /// Examples of observer patterns /// </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.OnTemperatureChange; 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 NewTemprature { get; set; } } //C#Once the compiler encounters the event keyword, it generates CLI code equivalent to the code in the Thermostat 2 class. 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> OnTemperatureChange { add { add_OnTemperatureChange(value); } remove { remove_OnTemperatureChange(value); } }*/ } //Implementation of Custom Events public class Thermostat3 { protected EventHandler<TemperatureArgs> onTemperatureChange; public event EventHandler<TemperatureArgs> OnTemperatureChange { add { //Last registered subscribers are first notified when notified Delegate.Combine(value, onTemperatureChange); } remove { Delegate.Remove(onTemperatureChange, value); } } } /// <summary> /// Regulator, used to control the switch between heater and cooler /// </summary> public class Adjust { public Adjust(string controllerName, float temperature, Func<float, float, bool> predicate) { ControllerName = controllerName; Temperature = temperature; predicateSwitch = predicate; } /// <summary> /// The name of the controller, which 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"); } }