Introduction. Net delegates (1) ---- Microsoft .NET platform series articles are indeed one of the most useful programming mechanisms so far. C The QSort function at runtime uses the callback function to sort the array elements. In Windows, the callback function is a window process, hook process, asynchronous process call, and current Microsoft .NET framework, the callback method is used in the whole callback process. People can register the callback method to obtain load / unloading notifications, unwaled exception notifications, database / window status modification notifications, file system modification notifications, menu item selection, completed asynchronous operation notifications, filter a set of entries, and more. In C / C , the address of a function is the memory address. This address does not include any other assignment information, such as the number of parameters, parameter type, and the return value of the function, and the call specification of this function. Briefly, the C / C callback function is not type safe. In the .NET framework, the reuse of the callback function is the same as it in Windows non-controlled programming. Different are the type security mechanisms of delegates in the .NET framework. Let's first study the delegated statement. The following code shows how to declare, create and use delegation: //
Using system;
Using system.winforms; // In the beta2 version: System.Windows.Forms
Using system.io;
Class set {
Private Object [] items;
Public set (INT32 NumItems) {
Items = new object [NumItems];
For (int32 i = 0; i Items [i] = i; } / / Define Feedback, type is delegate // (Note: This type is nested in the set class) Public Delegate Void Feedback Object value, int32 item, int32 numItems; Public void processItems (feedback feedback) { For (int32 item = 0; item IF (feedback! = NULL) { // Once the callback is specified, they call them. Feedback (items [items [item], item 1, items.length); } } } } Class app { [StathReadattribute] Static void main () { StaticCallbacks (); InstanceCallbacks (); } Static void staticcallbacks () { // Create a set object, five projects Set setofItems = new set (5); // Project, Feedback = NULL SetofItems.ProcessItems (NULL); Console.writeLine (); // Project, feedback = console SetofItems.ProcessItems (new set.feedback (app.feedbacktoconsole); Console.writeLine (); // Project, Feedback = MSGBOX SetofItems.ProcessItems (new set.feedback (app.feedbacktomsgbox); console.writeline (); // Project, Feedback = Console and Msgbox SET.FEEDBACK FB = NULL; FB = New set.Feedback (app.feedbacktoconsole); FB = new set.Feedback (app.feedbacktomsgbox); SETOFITEMS.PROCESSITEMS (FB); Console.writeLine (); } Static void feedbacktoconsole Object value, int32 item, int32 numItems) { Console.writeline ("Processing Item {0} of {1}: {2}.", Item, NumItems, Value; } Static void feedbacktomsgbox Object value, int32 item, int32 numItems) { Messagebox.show (string.format ("processing item {0} of {1}: {2}.", Item, NumItems, Value); } Static void instancecallbacks () { // Create a set object, five elements Set setofItems = new set (5); // Project, feedback = file App AppOBJ = New App (); SetOfItems.ProcessItems (New Set.FeedBackTofile); Console.writeLine (); } Void feedbacktofile Object value, int32 item, int32 numItems) { StreamWriter SW = New StreamWriter ("status", true); SW.WRITELINE ("Processing Item {0} of {1}: {2}.", Item, NumItems, Value; SW.CLOSE (); } } // Note the top SET class in the code. Suppose this class contains a group that will be processed separately. When you create a set object, the number of items it wants to manipulate to its constructor. The function is then constructed to create an object (Objects) array and initialize each integer value. In addition, the SET class defines a public delegation, which is the signature of a callback function. In this example, the delegation feedback determines a method with three parameters (the first parameter Object, the second and third parameters are INT32 types) and return VOID. In a sense, delegate is very similar to the type definition of a function address in C / C . In addition, the SET class defines a public method: ProcessItems. This method has a parameter feedback - a reference to the Debate FEEDBACK object. ProcessItems iterates all array elements and calls the callback method for each element (which tone method specifies by the feedback variable), this callback method is passed to handle the item value transmitted by the callback method in different ways, the number of items transmitted by the callback method. And the number of elements in the array. It can be seen that the callback method can handle each item in any way it chooses. The use of delegation calls static method staticcallbacks method demonstrates the callback delegation with a variety of different ways. This method first constructs a SET object tells it an array of objects that have five object elements. Then call ProcessItems, in the first call, its feedback parameter is null. ProcessItems presents a method that implements actions for each SET manipulated project. In the first example, because the feedback parameter is NULL, no callback method is called when each item is processed. In the second example, a new SET.FEEDBACK delegation object is created. This delegate is a method wrapper that allows the call to be indirectly called via this wrapper. For the constructor of the type feedback, the name of the method is passed as the parameter of the constructor; this means that the method is packaged. Then, the reference returned from the New operator is passed to ProcessItems. Now, when ProcessItems is executed, it calls the App type FEEDBACKTOCONSOLE method to handle each item in the collection. FeedbackToconsole simply outputs a string to the console, indicating which item is processed and what is the value of this project. The third example is basically the same as the second example. The only difference is that the Feedback delegate object is packaged another method: App.FeedBackTomsgbox. This method creates a string, which project is derived by this string and what is the value of this item. Then display this string in a message box. The fourth example is also the last example of static call demonstrating how the delegation links together form a chain. In this example, first create a reference variable FB of a FEEDBACK delegate object and initialize it to NULL. This variable points to the head of delegated a list. NULL value indicates no node in the linked list. Then, construct the FeedBack delegate object, packaged from this object to the call to the App feedbackToconsole method. C #, = operator is used to add objects to the linked list of FB references. Fb points to the header of the linked list at this time. Finally, construct another Feedback delegate object, packaged by this object to the call of the App feedbackTomsGbox method. The = operator in the C # is again used to add objects to the lin list of the FB reference, and FB is updated by the header of the new list. Now, when ProcessItems is executed, it is passed to the head pointer of the Feedback delegated a list. Inside the ProcessItems, the code line that calls the callback method actually terminates the callback method that calls all the delegated object packages in the linked list. That is to say, for each item iteration, feedbacktoconsole is called, and feedbacktomsgbox is then called immediately. In the subsequent article, I will discuss the processing mechanism of delegated chains in detail. One thing is important, that is, everything in this example is safe, for example, when constructing the Feedback delegate object, the compiler guarantees the FeedbackToconsole and FeedBackTomSgbox methods of the app to be exact prototypes, like the FeedbackTomsGbox method. same. There must be three parameters (Object, INT32, and INT32), and both methods must have the same return type (Void). If the method prototype does not match, the compiler will issue the following error message: "Error CS0123: The signature of method 'app.FeedBackTomsgbox ()' Does not match this delegate type." - means app.FeedBackTomsgbox () method The signature does not match the type of delegate. Calling an instance method We discussed how to use the delegation call static method. But delegate can also be used to call an instance method of a particular object. When the instance method is called, the delegate needs to know the object of the object it wants to operate with the method. In order to understand the callback mechanism of the example method, let's look back at the InstanceCallbacks method in the front code. This code is extremely similar to the situation of static methods. Note After the SET object is created, the App object is created. This app object is only created, and there is no other content in an example purpose. When the new FeedBack delegate object is created, its construct is completely passed to AppObj.FeedBacktofile. This will result in this delegate package to the feedbacktofile method, feedbacktofile is an instance method (non-static). When this instance method is called, the object referenced by the AppObj is operated (as a hidden pass parameter). The role of the feedbacktofile method is a bit like FeedbackToconsole and feedbacktomsgbox, which is different that it opens a file and adds the processed project string to the file end. Unveiled the delegated mystery from the surface, the delegation seems to be easy to use: delegate keyword definitions with C #, constructed in a similar way, using a similar method called syntax call callback method (different is not Use the method name, but use the variable that refers to the appointment object). However, the actual operating mechanism of delegated is more complicated than the process described in the above example. The compiler and public language runtime (CLR) have hidden these complexity after the scenes, in which part of us will focus on how compiler and CLR are coordinated to achieve the delegation mechanism. These knowledge will greatly enrich your understanding of delegates and these knowledge will tell you how to effectively use them. We will also involve some additional features that can be used in programming. We still started from the following line: Public Delegate Void Feedback (Object Value, INT32 Item, INT32 NumItems); when the compiler sees one row code, it produces a complete class definition, this defined code is like the following This looks: // Public class feedback: system.multicastdeLegate {// constructor Public Feedback (Object Target, INT32 Methodptr); / / Method is the same as the prototype described by the source code Public void Virtual Invoke Object value, int32 item, int32 numItems; // Method allows for asynchronous callback, and the subsequent article will discuss these methods Public Virtual IASYNCRESULT BeginInvoke Object value, int32 item, int32 Numitems, AsyncCallback Callback, Object Object); Public Virtual Void endInvoke (IASYNCRESULT RESULT); } // In fact, you can find that the compiler does automatically generate this class by using the ILDASM.EXE program check the result module (Figure 3). Figure 3 Check the class generated by the compiler In this example, the compiler has defined a class called feedback from the System.MulticastDelegate type, which is defined in the Framework Class Library. You know, all delegated types are derived from MulticastDelegate. In this example, the Feedback class is a public type because it is defined as public code in the source code. If you define private (Private) or protected) type, the FEEDBACK class generated by the compiler will also be a private or protected type. You should notice that delegated categories may define in a class (if the feedback is defined in the SET class); delegation may also be defined as a global type. Essentially, delegate can be regarded as a class, and can define anywhere in the defined class. Because all delegates are derived from MulticastDelegate, they inherit the domain, attributes, and methods of MulticastDelegate. In all of these members, you have to pay special attention to three private (private): for delegate types: Domain Type Description_targetsystem.Object The object that should be operated when the function is called. Used for example method callback _MethodPTRSystem.Int32 internal integer, CLR with it to mark the callback method _prevsystem.multicastDelegate refers to another delegated object, usually null All delegates have two parameters constructor: a parameter is an object reference, one is an integer of the callback method. However, if you check the source code, you will find that the delivery value of app.FeedBackToconsole or AppObj.FeedBackTofile is made. Your sensitive will tell you this code can't compile! However, the compiler knows that a delegate is created, and the compiler parses the source code to determine which object and method referenced. The object reference is passed to the target parameter, and the method indicated by a particular INT32 value (obtained from a MethodDef or MethodRef metadata symbol) is passed to the MethodPtr parameter. For static methods, NULL is passed to target parameters. Inside the constructor, the two parameters are stored in their corresponding private (private) domains. In addition, the constructor is placed in NULL. This domain is used to create a multicastdeLegate object linked list. Now we temporarily ignore the _prev domain, which will discuss it in detail in subsequent articles. Each delegate object is actually a method wrapper that is operated when the method is called. MulticastDelegate class defines two read-only public instance properties: Target and Method. Given a delegate object reference, you can query these properties. If the method is called, the target attribute returns a reference to the object to be operated. If the method is static, Target returns NULL. The method of the method returns the System.Reflection.MethodInfo object indicating the callback method. You can use this information in several ways. One way is to check if a delegated object references a specific type of instance method: // Boolean DelegateReferStoinstanceMethodOfType MulticastDelegate D, Type Type) { Return ((D. Target! = null) && d.target.gettype == Type); } // You should also write code to check if the callback method is dedicated (such as feedbacktomsgbox): // Boolean DelegateReferStomethodofname MulticastDelegate D, String MethodName) { Return (D.Method.Name == MethodName); } // Now you know how to construct a delegate object, let us talk about how the callback method is called. For convenience, we still use the ProcessItems: // of the set class. Public void processItems (feedback feedback) { For (INT32 item = 1; item <= items.length; item ) { IF (feedback! = NULL) { // If you specify any callback, call them Feedback (items [item], item, items.length; } } } // Note The line of code below the line is called the callback method. Take a closer look, it calls the feedback function and pass three parameters. But feedback does not exist. Again, the compiler knows that Feedback is a variable that references a delegate object, and the compiler generates actual code to call the INVOKE method of the delegate object. In other words, the compiler sees the following line code: feedback (items [items], item, items.length; the result produced by the compiler is the same as the result of the line source code below: feedback.invoke (items [Items Items, item, items.length; in fact, you can find this by checking the processitems code results (Figure 5) by using the ILDASM.EXE program (Figure 5). The PROCESSITEMS Figure 5 of the SET class after Differential Decomposition shows the Microsoft Intermediary language for the ProcessItems method in the SET type. Among the red arrows point to the instruction to call the Invoke method of set.Feedback. If you modify the source code to explicitly call the Invoke method, the C # compiler error, the error message is: "Error CS1533: Invoke Cannot Be Called Directly on a delegate" - meaning invoke cannot be called directly for a delegation. C # does not allow you to explicitly call Invoke (but, but other compilers can be). You will also remember that when the compiler defines the Feedback class, it also defines the Invoke method. When INVOKE is called, it uses a private _target and _methodptr domain to call the desired method for a particular object. Note that the signature of the Invoke method is fully matched with the delegated signature. That is, the Feedback delegates three parameters and returns Void, then the Invoke method must also bring three identical parameters and return Void.