Callback Function
The callback function is indeed one of the most useful programming mechanisms so far. The qsort function in C Runtime uses the callback function to sort array elements. In Windows, the callback function is also a window process, hook process, asynchronous process call, and currently required by the Microsoft. NET Framework. The callback method is used throughout the entire callback process. People can register callback methods to receive loading/unloading notifications, unhandled exception notifications, database/window status modification notifications, file system modification notifications, menu item selection, and completed Asynchronous Operation notifications, filter a group of entries.
In C/C ++, the address of a function is the memory address. This address does not contain any additional information, such as the number of parameters, parameter types, return value types, and call specifications of the function. In short, C/C ++ callback functions are not of type security.
In the. NET Framework, callback functions are reused in the same way as windows uncontrolled programming. The difference is that the. NET Framework provides a type security mechanism called delegation. Let's first study the delegate statement. The following code demonstrates how to declare, create, and use delegates:
//
Using system;
Using system. winforms; // In beta2, the value is system. Windows. forms.
Using system. IO;
Class set {
Private object [] items;
Public set (int32 numitems ){
Items = new object [numitems];
For (int32 I = 0; I <numitems; I ++)
Items [I] = I;
}
// Define the feedback type as 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 <items. length; item ++ ){
If (feedback! = NULL ){
// Call them once callback is specified
Feedback (items [item], item + 1, items. Length );
}
}
}
}
Class app {
[Stathreadattribute]
Static void main (){
Staticcallbacks ();
Instancecallbacks ();
}
Static void staticcallbacks (){
// Create a set object with five projects
Set setofitems = new set (5 );
Streamwriter Sw = new streamwriter ("status", true );
Sw. writeline ("processing item {0} of {1 }:{ 2 }.",
Item, numitems, value );
Sw. Close ();
}
}
//
Pay attention to the set class at the top of the Code. Suppose this class contains a group of projects that will be processed separately. When you create a set object, pass the number of items it wants to manipulate to its constructor. Then the constructor creates an object (objects) array and initializes each integer value.
In addition, the set class defines a Public Delegate, which indicates the signature of a callback function. In this example, assign feedback to determine a method with three parameters (the first parameter is object, the second and third parameters are of the int32 type) and return void. In a sense, delegates are very similar to the type definition (typedef) that represents 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 delegate feedback object. Processitems iterates through all the array elements and calls a callback method for each element (the callback method is specified by the feedback variable). This callback method is passed, in this way, the project value, the number of projects, and the number of elements in the array are processed in different ways. We can see that the callback method can process every project in any way it chooses.
Use delegates to call static methods
The staticcallbacks method demonstrates different callback delegation methods. This method first constructs a set object and tells it to create an array with five object elements. Then processitems is called. In the first call, its feedback parameter is null. Processitems presents a method that implements some action for the project operated by each set. In the first example, because the feedback parameter is null, no callback method is called when processing each project.
In the second example, a new set. Feedback delegate object is created. This delegate object is a method package that allows indirect call of methods through this package. For the feedback constructor of the type, the method name (App. feedbackconsole) is passed as a parameter of the constructor; this indicates that the method is encapsulated. Then, the reference returned from the new operator is passed to processitems. Now, when processitems is executed, it calls the feedbacktoconsole method of the app type to process each project in the set. The feedbacktoconsole simply outputs a string to the console, indicating the project to be processed and the value of the project.
The Third and Second examples are basically the same. The only difference is that the feedback delegate object wraps another method: App. feedbacktomsgbox. This method creates a string that indicates the project to be processed and the project value. Then, the string is displayed in an information box.
The fourth example is static call. The last example demonstrates how to link delegates together to form a chain. In this example, first create a feedback to delegate the object's reference variable FB and initialize it to null. This variable points to the delegate linked list header. The null value indicates that there are no nodes in the linked list. Then, construct the feedback delegate object, which wraps the call to the feedbacktoconsole method of the app. In C #, The + = Operator is used to add objects to the linked list referenced by FB. FB points to the head of the linked list.
Finally, construct another feedback delegate object, which wraps the call to the app feedbacktomsgbox method. The + = Operator in C # is used again to add objects to the linked list referenced by FB, And the FB is updated by the new linked list header. Now, when processitems is executed, the header pointer assigned by feedback to the chain table is passed to it. In processitems, the code line that calls the callback method actually terminates calling all the callback Methods encapsulated by Delegate objects in the linked list. That is to say, feedbacktoconsole is called for each project to be iterated, and feedbacktomsgbox is immediately called. In subsequent articles, I will discuss in detail the handling mechanism of the delegate chain.
It is very important that everything in this example is type-safe. For example, when constructing a feedback delegate object, the compiler ensures that the feedbacktoconsole and feedbacktomsgbox methods of the app have the exact prototype, as defined by feedback delegation. Both methods must have 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 sends the following error message: "error cs0123: the signature of method 'app. feedbacktomsgbox () 'does not match this delegate
Type ." -- The signature of the app. feedbacktomsgbox () method does not match the delegate type.
Call instance method
We have discussed how to use delegates to call static methods. However, delegates can also be used to call instance methods of specific objects. When calling an instance method, delegate the instance of the object that needs to be known to be operated by the method.
To understand the callback mechanism of the instance method, let's look back at the instancecallbacks method in the previous code. This code is very similar to static methods. Note that after the set object is created, the app object is created. This app object is only created for the purpose of the example. When a new feedback delegate object is created, its construction is complete and uploaded to appobj. feedbacktofile. This will cause this delegate to wrap references to the feedbacktofile method. feedbacktofile is an instance method (non-static ). When this instance method is called, the object referenced by appobj is operated (as a hidden passing parameter ). The feedbacktofile method works a bit like feedbacktoconsole and feedbacktomsgbox. The difference is that it opens a file and adds the processed project string to the end of the file.
Unveil the secret of delegation
On the surface, Delegation seems easy to use: Use the C # Delegate keyword to define, and use a method similar to the new operator to construct their instances, call the callback method using a syntax similar to a method call (the difference is that the method name is not used, but the variable that refers to the delegate object ).
However, the actual operating mechanism of delegation is much more complex than the process described in the preceding example. Many of the operations performed by the compiler and the Common Language Runtime (CLR) behind the scenes hide these complexities. In this section, we will focus on how the compiler and CLR work together to implement the delegate mechanism. This knowledge will greatly enrich your understanding of delegates and will tell you how to use them effectively. We will also involve some additional delegate features that can be used in programming.
Let's start with the following line of code:
Public Delegate void feedback (
Object value, int32 item, int32 numitems );
When the compiler sees a line of code, it will generate a complete class definition, the code of this definition will look like below:
//
Public class feedback: system. multicastdelegate {
// Constructor
Public Feedback (object target, int32 methodptr );
// The method is the same as the prototype described in source code.
Public void virtual invoke (
Object value, int32 item, int32 numitems );
// Methods allow asynchronous callback. These methods will be discussed in subsequent articles.
Public Virtual iasyncresult begininvoke (
Object value, int32 item, int32 numitems,
Asynccallback callback, object );
Public Virtual void endinvoke (iasyncresult result );
}
//
When using the ildasm.exe program to check the result module (3), you can find that the compiler automatically generates this class.
Figure 3 check the class generated by the compiler
In this example, the compiler has defined a class named feedback derived from the system. multicastdelegate type, which is defined in the framework class library. You must know that all Delegate types are derived from multicastdelegate. In this example, the feedback class is public because its type is defined as public in the source code. If you use a private or protected type, the feedback class generated by the compiler will also be private or protected. You should note that the delegate class may be defined in a class (for example, feedback is defined in the Set class), and the delegate may be defined as a global class. In essence, delegates can be viewed as classes and can be defined anywhere in the definition class.
Because all delegates are derived from multicastdelegate, they inherit the domain, attributes, and methods of multicastdelegate. Among all these Members, pay special attention to three private domains:
Private domains used for delegation:
Domain type description
_ Target system. Object indicates the object to be operated when the callback function is called. Used for instance method callback
_ Methodptr system. int32 internal integer, which is used by CLR to indicate the method to be called back
_ Prev system. multicastdelegate refers to another delegate object, usually null
All delegates have constructor on behalf of two parameters: one parameter is an object reference, and the other is an integer that refers to the callback method. However, if you check the source code, you will find that the values used for passing app. feedbacktoconsole or appobj. feedbacktofile are clear. Your sensitivity tells you that this Code cannot be compiled!
However, the compiler knows that a delegate is created, and the compiler parses the source code to decide which object and method to reference. The object reference is passed as the target parameter, and the method marked with a specific int32 value (obtained from a methoddef or methodref metadata symbol) is passed as the methodptr parameter. For static methods, null is passed as the target parameter. Inside the constructor, these two parameters are stored in their corresponding private domain.
In addition, the constructor sets this field to null. This field is used to create a multicastdelegate object linked list. Now we will ignore the _ Prev field for the moment and will discuss it in detail in subsequent articles.
Each delegate object is actually a method wrapper. When a method is called, the target object is operated. The multicastdelegate class defines two read-only public instance attributes: Target and method. Given a delegate object reference, you can query these attributes. If the method is called back, the target attribute returns a reference to the object to be operated. If the method is static, target returns NULL. The method property returns the system. reflection. methodinfo object indicating the callback method.
You can use this information in several ways. One way is to check whether a delegate object references a specific type of instance method:
//
Boolean delegatereferstoinstancemethodoftype (
Multicastdelegate D, type ){
Return (D. Target! = NULL) & D. Target. GetType = type );
}
//
You should also write code to check whether the callback method is named by a special name (such as feedbacktomsgbox ):
//
Boolean delegatereferstomethodofname (
Multicastdelegate D, string methodname ){
Return (D. method. Name = methodname );
}
//
Now that you know how to construct a delegate object, let's talk about how the callback method is called. For convenience, we still use
Processitems in the Set class:
//
Public void processitems (FEEDBACK feedback ){
For (int32 item = 1; item <= items. length; item ++ ){
If (feedback! = NULL ){
// If any callback is specified, call them
Feedback (items [item], item, items. Length );
}
}
}
//
The line of code below the comment line is to call the callback method. Take a closer look at the code. It calls the feedback function and passes three parameters. However, feedback does not exist. Once again, the compiler knows that feedback is a variable that references a delegate object, and the compiler will generate actual code to call the invoke method of the delegate object. In other words, after the compiler sees the following line of code:
Feedback (items [item], item, items. Length );
The result produced by the compiler is the same as that produced by the following line of source code:
Feedback. Invoke (items [item], item, items. Length );
You can find this by using the ildasm.exe program to check the processitems code result (5.
Figure 5 processitems of the decomposed set class
Figure 5 shows the Microsoft mediation language used for the processitems method in the set type. The red arrow points to the command to call the set. Feedback invoke method. If you modify the source code to explicitly call the invoke method, the C # compiler reports an error with the error message "error cs1533: invoke cannot be called directly on a delegate "-- it means that invoke cannot be directly called for a delegate. C # You cannot explicitly call invoke (but other compilers can ).
You will remember that when the compiler defines the feedback class, it also defines the invoke method. When invoke is called, it uses the private _ target and _ methodptr fields to call desired methods for specific objects. Note that the signature of the invoke method must be exactly the same as that of the delegate signature. That is to say, if feedback delegates three parameters and returns void, the invoke method must also contain three identical parameters and return void.
Conclusion
This article discusses the basic concepts of delegation. Based on the content currently discussed in this article, you should be able to create and use them now. In subsequent articles, I will explain the delegation chain in the linked list, as well as some additional methods for multicastdelegate, such as the system. Delegate type and event ....... Wait for my good news.
This is the fourth article in the series. The first three articles are:
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.