C # Delegate Creator for reflection

Source: Internet
Author: User
ArticleDirectory
    • 1.1 create an open method delegate
    • 1.2 create a method delegate with the first parameter closed
    • 1.3 create a common method delegate

. Net reflection is a very powerful thing, but its efficiency is really not powerful. Many people have discussed this issue, including a variety of dynamicmethods and various efficiency tests. But in general, the solution is to use Expression Tree and delegate. createdelegate or emit constructs the delegate corresponding to the reflection operation to accelerate the reflection.

Although this article also discusses how to use delegation to accelerate the reflection call function, the focus is not on how to increase the call speed, but on how to intelligently construct the reflection delegate, finally, a convenient and easy-to-use delegate builder delegatebuilder is completed.

It is designed:

    1. Supports method call and constructor call to obtain or set attributes and obtain or set fields.
    2. Can construct a specific delegate type, not limited to func <object, object [], Object> or other func and action, because I personally like forced delegation, at the same time, a delegate similar to void mydeleagte (Params int [] ARGs) is sometimes necessary. To support the ref and out parameters, you must use a custom delegate type.
    3. Wildcard methods are supported, because using reflection to select a generic method is quite complicated (unless there is no method of the same name), and makegenericmethod is required.
    4. Supports explicit type conversion. When constructing a delegate for some private class instance methods, the instance itself must be passed in using objects.

Points 3 and 4, the previous articles titled whether implicit or forced type conversion between types of C # judgment has been resolved and integrated into powerbinder, here, we only need to solve and. This article will discuss how to construct the corresponding delegate based on reflection.

As for the current results, delegatebuilder is very convenient to use. The following are some examples:

Class program {Public Delegate void mydelegate (Params int [] ARGs); public static void testmethod (INT value) {} public void testmethod (uint value) {} public static void testmethod <t> (Params T [] Arg) {} static void main (string [] ARGs) {type = typeof (Program ); action <int> m1 = type. createdelegate <action <int> ("testmethod"); M1 (10); program P = new program (); Action <program, uint> m2 = type. createdelegate <action <program, uint> ("testmethod"); M2 (p, 10); Action <object, uint> m3 = type. createdelegate <action <object, uint> ("testmethod"); M3 (p, 10); Action <uint> M4 = type. createdelegate <action <uint> ("testmethod", P); M4 (10); mydelegate M5 = type. createdelegate <mydelegate> ("testmethod"); M5 (0, 1, 2 );}}

It can be said that the effect is good. The createdelegate usage and delegate here. createdelegate is identical, but has many functions. You can use the delegate type, type, and membername to construct any required delegation, eliminating the need to retrieve type members.

HereNote:This class is easy to use, but the implementation is complicated behind it. Therefore, it is normal for various bugs and inference errors that have not been found.

Me againAdd:Although I am not going to discuss efficiency here, many of my friends have a bit of difficulty with efficiency. I will explain this question in detail.

The first question: why do we use delegation instead of reflection. If you have decompilation software such as reflector, you can look at the system. reflection. runtimemethodinfo. to implement the invoke method, it first needs to check the parameters (check the default parameters, type conversion, and so on), then checks various flags, and then calls unsafeinvokeinternal to complete the real call process, obviously, it is much slower than calling methods directly. However, if a delegate is constructed using methods such as Expression Tree, it is equivalent to a method call with only one more layer, with no performance loss (it is said that emit can be used better and faster ), therefore, we need to use delegation instead of reflection.

The second question: When can I use delegation instead of reflection. Now suppose there is a park, and its ticket price is 1 yuan. It also has a lifetime ticket with a fare of 20 yuan. If I just want to see it, it is very likely that I will not go in later, then it is most appropriate to spend 1 yuan directly. But if I want to walk around every day, it is more appropriate to spend 20 yuan to buy a lifetime ticket.

Correspondingly, 1 yuan of tickets are reflection, and 20 yuan of lifetime tickets are Commission-if I only call a method occasionally, I just need to use reflection directly, in any case, the loss is not very large. If I need to call it frequently, it is better to take some time to construct a delegate. Although the process of constructing a delegate is slow, it will take a lifetime.

Third question: how to test the delegate and reflection efficiency. The premise of test efficiency is to assume that a method needs to be called frequently, otherwise there is no need to use delegation at all. The basic structure is as follows:

Stopwatch Sw = new stopwatch (); Type type = typeof (Program); Sw. start (); Action <int> action = type. createdelegate <action <int> ("testmethod"); For (INT I = 0; I <10000; I ++) {Action (I);} SW. stop (); console. writeline ("delegatebuilder: {0} ms", SW. elapsedmilliseconds); Sw. start (); methodinfo method = type. getmethod ("testmethod"); For (INT I = 0; I <10000; I ++) {method. invoke (null, new object [] {I});} SW. stop (); console. writeline ("Reflection: {0} ms", SW. elapsedmilliseconds );

Here, we put the process of constructing the delegate and the process of obtaining methodinfo from reflection out of the loop because they only need to be obtained once, it can be used all the time (that is, the so-called "preprocessing "). As for the time to place them between the start and stop of stopwatch, You can see whether you want to take the time required for preprocessing into consideration.

Now I can think of only three questions. If you have any other questions, contact me.

Let's analyze how to construct the corresponding delegate for reflection. For simplicity, I will use Expression Tree to construct the delegate, which is easier to read and less efficient than emit. If you are not familiar with expression, refer to expression class.

1. Delegate to create a method from methodinfo

First, let's talk about how to create a method delegate, because the method delegate is obviously the most common and basic. The delegate class provides a good reference for us. Its createdelegate method has ten reloads. The relationships between these reloads can be shown in the figure below. Their detailed explanations are as follows:

Figure 1 delegate. createdelegate

These methods are indeed very powerful and easy to use, even though they are not strong enough in my opinion :). For ease of use, the behavior of delegate creation method of your method should also be similar to the delegate. createdelegate method. Therefore, we will first analyze the usage of the createdelegate method, and then explain how to create a delegate by yourself.

1.1 create an open method delegate

The functions of createdelegate (type, methodinfo) and createdelegate (type, methodinfo, Boolean) are the same and can be used to create static method delegation, or explicitly provide the first hidden parameter of the instance method (called the Open instance method, from.. NET Framework 2.0 and later. Take the following classes as an example:

 
Class testclass {public static void teststaticmethod (string value) {} public void testmethod (string value ){}}

To create a delegate for the teststaticmethod method, use the action <string> delegate type,CodeIs

Delegate. createdelegate (typeof (Action <string>), type. getmethod ("teststaticmethod "))

The result of the delegate is the same as that of teststaticmethod (arg1.

To create a delegate for the testmethod method, you must use the action <testclass, string> delegate type. The first parameter indicates the instance of testclass on which you want to call the method:

 
Delegate. createdelegate (typeof (Action <testclass, string>), type. getmethod ("testmethod "))

The result of the delegate is the same as that of arg1.testmethod (arg2.

The usage of this method is clear and easy to implement:

First, construct a closed generic method for the open generic method.2.2.2 processing generic methodsTheAlgorithmSame. I will not go into details here.

Next, you can directly use expression. Call to create a delegate for a method call, and add a forced type conversion (expression. Convert) for each parameter. Note that if methodinfo is an instance method, the first parameter must be used as an instance. Finally, the method constructed using expression should be similar:

// Method corresponds to the static method. Returntype methoddelegate (pt0P0, PT1P1,..., PTNPN) {Return method (T0) P0, (T1) P1,..., (TN) PN);} // Method corresponds to the instance method. Returntype methoddelegate (pt0P0, PT1P1,..., PTNPN) {Return (T0) P0). Method (T1) P1,..., (TN) PN);}

The core method for constructing open method delegation is as follows:

Private Static delegate createopendelegate (type, methodinfo invoke, parameterinfo [] invokeparams, methodinfo method, parameterinfo [] methodparams) {// required parameter quantity match, the first parameter of the instance method is used to pass the instance object. Int skipidx = method. isstatic? 0: 1; if (invokeparams. length = methodparams. length + skipidx) {If (method. isgenericmethoddefinition) {// construct a closed Method for a generic method. Skip the first parameter for an instance method. Type [] paramtypes = getparametertypes (invokeparams, skipidx, 0, 0); method = method. makegenericmethodfromparams (methodparams, paramtypes); If (Method = NULL) {return NULL;} methodparams = method. getparameters ();} // list of parameters of the method. Parameterexpression [] paramlist = getparameters (invokeparams); // construct a list of call parameters. Expression [] paramexps = getparameterexpressions (paramlist, skipidx, methodparams, 0); If (paramexps! = NULL) {// The instance object that calls the method. Expression instance = NULL; If (skipidx = 1) {instance = converttype (paramlist [0], method. declaringtype); If (instance = NULL) {return NULL ;}} expression methodcall = expression. call (instance, method, paramexps); methodcall = getreturn (methodcall, invoke. returntype); If (methodcall! = NULL) {return expression. Lambda (type, methodcall, paramlist). Compile () ;}} return NULL ;}
1.2 create a method delegate with the first parameter closed

Createdelegate (type, object, methodinfo) and createdelegate (type, object, methodinfo, Boolean) are the most flexible methods for creating delegation. You can create Delegation for static or instance methods, you can provide or do not provide the first parameter. The following is an example of all usage:

 
Class testclass {public static void teststaticmethod (string value) {} public void testmethod (string value ){}}

For teststaticmethod (static method:

    1. If firstargument is not null, it is passed to the first parameter of the method each time a delegate is called. In this case, it is called closed by the first parameter, the delegate signature must include all parameters except the first parameter of the method.

      Delegate. createdelegate (typeof (Action), "str", type. getmethod ("teststaticmethod "))

      The result of the delegate is the same as that of teststaticmethod (firstargument.

    2. If firstargument is null and the delegate matches the signature of the method (that is, all parameter types are compatible), it is called an open static method delegate.
      Delegate. createdelegate (typeof (Action <string>), null, type. getmethod ("teststaticmethod "))

      The result of the delegate is the same as that of teststaticmethod (arg1.

    3. If firstargument is null and the signature of the delegate starts with the second parameter of the method, and other parameter types are compatible, it is called a closed delegate through null reference.
      Delegate. createdelegate (typeof (Action), null, type. getmethod ("teststaticmethod "))

      The result of the delegate is the same as that of teststaticmethod (null.

For testmethod (instance method:

    1. If firstargument is not null, firstargument is passed to the hidden instance parameter (this), which becomes a closed instance method. The signature of the delegate must match the signature of the method, usage:

      Delegate. createdelegate (typeof (Action <string>), new testclass (), type. getmethod ("testmethod "))

      The result is the same as firstargument. testmethod (arg1.

    2. If firstargument is null and the delegate displays the first hidden parameter (this) that contains the method, it is called an open instance method delegate.
      Delegate. createdelegate (typeof (Action <testclass, string>), null, type. getmethod ("testmethod "))

      The delegate effect is the same as that of arg1.testmethod (arg2.

    3. If firstargument is null and the signature of the delegate matches the signature of the method, it is called a closed delegate through null reference.
      Delegate. createdelegate (typeof (Action <string>), null, type. getmethod ("testmethod "))

      This usage is strange. This usage is similar to calling the instance method (null. testmethod (OBJ) for an empty instance. In the method body, this is null, which is not very useful in practice.

From the above six points, we can determine how to construct the delegate Based on the static method or instance method of the method and the matching method between the delegate and the method signature. The following is the flowchart of the judgment:

Figure 2 flowchart of method Delegation

For Open static or instance methods, you can use the method completed in the previous section. For closed static or instance methods, the method is similar, you only need to use firstargument as the first parameter of the static method or as an instance. In the flowchart, we can use the null reference closed instance method because expression cannot call the instance method for null, only delegate can be used. createdelegate to generate the delegate, and then set another layer of the delegate to implement forced type conversion. This will certainly be less efficient, but after all, this usage is basically impossible to see, here only to ensure the unification with createdelegate.

1.3 create a common method delegate

Here I add a method to create a general method delegate. The declaration of this delegate is as follows:

 
Public Delegate object methodinvoker (object instance, Params object [] parameters );

With this delegate, you can call any method. To implement this method, you only need to use expression to construct a method similar to the following one.

 
Object methoddelegate (object instance, Params object [] parameters) {// check the Parameters Length. If (parameters = NULL | parameters. length! = N + 1) {Throw new targetparametercountexception () ;}// call the method. Return instance. Method (T0) Parameters [0], (t1) Parameters [1],..., (tN) Parameters [N]);}

for generic methods, it is obvious that the generic parameter inference cannot be performed, and an error is returned directly. For static methods, ignore the instance parameter directly.

Public static methodinvoker createdelegate (this methodinfo method) {exceptionhelper. checkargumentnull (method, "method"); If (method. isgenericmethoddefinition) {// do not bind open generic methods. Throw exceptionhelper. bindtargetmethod ("method");} // instance of the method to be executed. Parameterexpression instanceparam = expression. parameter (typeof (object); // method parameters. Parameterexpression parametersparam = expression. parameter (typeof (object []); // construct the parameter list. Parameterinfo [] methodparams = method. getparameters (); expression [] paramexps = new expression [methodparams. length]; for (INT I = 0; I <methodparams. length; I ++) {// (Ti) parameters [I] paramexps [I] = converttype (expression. arrayindex (parametersparam, expression. constant (I), methodparams [I]. parametertype);} // The static method does not require an instance. The instance method requires (tinstance) instanceexpression instancecast = method. isstatic? Null: converttype (instanceparam, method. declaringtype); // call a method. Expression methodcall = expression. Call (instancecast, method, paramexps); // Add parameter quantity detection. Methodcall = expression. block (getcheckparameterexp (parametersparam, methodparams. length), methodcall); Return expression. lambda <methodinvoker> (getreturn (methodcall, typeof (object), instanceparam, parametersparam ). compile ();}
Ii. constructorinfo: Create constructor delegate

It is easy to create a constructor delegate. The constructor does not have a distinction between static and instance, there is no generic method, and the signature of the Delegate and constructor must match, the implementation is like1.1 create an open method delegateBut this is the actual expression. New Method Used Instead of expression. Call.

Public static delegate createdelegate (type, constructorinfo ctor, bool throwonbindfailure) {predictionhelper. checkargumentnull (ctor, "ctor"); checkdelegatetype (type, "type"); methodinfo invoke = type. getmethod ("INVOKE"); parameterinfo [] invokeparams = invoke. getparameters (); parameterinfo [] methodparams = ctor. getparameters (); // the number of parameters must match. If (invokeparams. Length = methodparams. Length) {// list of constructor parameters. Parameterexpression [] paramlist = getparameters (invokeparams); // construct a list of call parameters. Expression [] paramexps = getparameterexpressions (paramlist, 0, methodparams, 0); If (paramexps! = NULL) {expression methodcall = expression. New (ctor, paramexps); methodcall = getreturn (methodcall, invoke. returntype); If (methodcall! = NULL) {return expression. lambda (type, methodcall, paramlist ). compile () ;}}if (throwonbindfailure) {Throw exceptionhelper. bindtargetmethod ("ctor");} return NULL ;}

Similar to the general method delegate, I also use the following delegate

Public Delegate object instancecreator (Params object [] parameters );

To create a delegate for a common constructor, which is similar to the implementation of the Delegate for a common method.

Public static delegate createdelegate (type, constructorinfo ctor, bool throwonbindfailure) {predictionhelper. checkargumentnull (ctor, "ctor"); checkdelegatetype (type, "type"); methodinfo invoke = type. getmethod ("INVOKE"); parameterinfo [] invokeparams = invoke. getparameters (); parameterinfo [] methodparams = ctor. getparameters (); // the number of parameters must match. If (invokeparams. Length = methodparams. Length) {// list of constructor parameters. Parameterexpression [] paramlist = getparameters (invokeparams); // construct a list of call parameters. Expression [] paramexps = getparameterexpressions (paramlist, 0, methodparams, 0); If (paramexps! = NULL) {expression methodcall = expression. New (ctor, paramexps); methodcall = getreturn (methodcall, invoke. returntype); If (methodcall! = NULL) {return expression. lambda (type, methodcall, paramlist ). compile () ;}}if (throwonbindfailure) {Throw exceptionhelper. bindtargetmethod ("ctor");} return NULL ;}
3. Create an attribute delegate from propertyinfo

With the creation method delegate as the basis, it is very easy to create the attribute delegate. If the delegate has a return value, it means to get the attribute. If the delegate does not return the value (the return value is typeof (void), it means to set the attribute. Then, use propertyinfo. getgetmethod or propertyinfo. getsetmethod to obtain the corresponding get or set accesser, and then call the delegate of the Creation method directly.

Closed attribute delegation is also very useful, so that you can bind the attribute instance to the delegate.

No general delegate is created for the attribute because the access to the attribute is divided into two parts: Get and set. These two parts cannot be effectively combined into one.

4. Create a field delegate from fieldinfo

When you create a field delegate, you cannot use the existing method. Instead, you must use expression. Assign to assign values to the field. Field delegation can also be divided into open field delegation and field delegation closed by using the first parameter. The judgment process is as follows:

Figure 3 field delegation Flowchart

The processing of a field is simple, that is, accessing the field through expression. Field, then assigning values to the field through expression. Assign, or directly returning the value of the field. The "reference closed instance field through null" shown in the figure is also because the instance field of the empty object cannot be accessed by code, which is obviously meaningless, however, in order to get the same result as the attribute closed by null reference, system is always thrown here. nullreferenceexception.

5. Create a member delegate from type

This method provides the most flexible way to create a member delegate. It can decide whether to create a method, constructor, attribute, or field delegate based on the given member name, bindingflags, and delegate signature.

It uses powerbinder in sequence. cast searches for methods, attributes, and fields matching the given delegate signature in type, and tries to construct the delegate for each matched member (use the method given in the previous four sections ). When a member successfully constructs a delegate, it is the final one.

Since powerbinder supports searching for generic methods and explicit type conversions, it naturally supports both generic and explicit type conversions when constructing a delegate.

The method for constructing the delegate by delegatebuilder is complete.Source codeIt can be seen that delegatebuilder. CS has a total of about 2500 rows, but most of them are comments and various method overloading (54 currently), and vs code measurements have only 509 rows.

Related Article

Contact Us

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.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.