Letter of authorization

Source: Internet
Author: User

Letter of authorization

Preface

  Delegation and events are two important knowledge in c #, which are often used in daily work. Next I will write two articles about my understanding of delegation and events.

Callback functions are a very useful programming mechanism, which is supported by many languages. A callback function is a function called by a function pointer. Generally, we pass the callback function as a parameter to another function. When some events occur or meet certain conditions, the caller executes the callback function to respond to the event or condition. To put it simply, perform the following steps to implement the callback function:

  1. Define a callback function.

2. register the callback function pointer to the caller.

3. When some events or conditions occur, the caller calls the callback function through the function pointer to process the events.

The callback mechanism has many applications, such as control events, asynchronous operation completion notifications, and so on. net implements the callback function mechanism through delegation. Compared with the callback mechanism of other platforms, the delegate provides more functions. For example, it ensures that the callback method is of type security, supports sequential calling of multiple methods, as well as calling static methods and instance methods.

I. Initial Knowledge Delegation

Before getting started with Delegation, I believe many people will feel strange and awkward to use. After understanding its nature, we know that many times the compiler is actually "playing tricks" behind it. The compiler has done a lot of work to reduce code compilation and make the Code look more elegant. Next, let's gradually gain an in-depth understanding of delegation.

Let's take a look at a simple piece of code:

// 1. define a delegate type delegate void TestDelegate (int value); static void Main (string [] args) {// 2. pass null ExecuteDelegate (null, 10); // 3. call the static method TestDelegate test1 = new TestDelegate (StaticFunction); ExecuteDelegate (test1, 10); // 4. call the instance method Program program = new Program (); TestDelegate test2 = new TestDelegate (program. instanceFunction); ExecuteDelegate (test2, 10); // 5. call multiple methods TestDelegate test3 = (TestDelegate) Delegate. combine (test1, test2); ExecuteDelegate (test3, 10);} // static method static void StaticFunction (int value) {Console. writeLine ("Call StaticFunction:" + value. toString ();} // instance method void InstanceFunction (int value) {Console. writeLine ("Call InstanceFunction:" + value. toString ();} // execute the delegate static void ExecuteDelegate (TestDelegate tg, int value) {if (tg! = Null) {tg (value );}}

Step 2: Use the delegate keyword to define a delegate type named TestDelegate. Its signature is: 1. the return value is void 2. There is an int type parameter. The callback function signature must be the same as the callback function signature. Otherwise, an error is reported during compilation.

Step 2: Call the method for executing the delegate and pass null. In fact, nothing is done. Here, the delegate can be used as a parameter and can be null, which seems to be similar to the reference type.

Step 2: Create a TestDelegate variable test1 with new and use the static method as the parameter. It complies with the delegate signature. We can infer that TestDelegate is a reference type.

Step 3 is similar to step 3, except that the parameter passed is an instance method. Therefore, you must first create the method object Program.

Step 2: Call the Delegate. Combine () method. You can specify the name to Combine multiple delegates. When test3 is called, all methods are executed in the order of its parameters. This method is sometimes very useful, because we are likely to execute multiple operations when an event occurs.

Through the above code, we can basically know that the delegate is used to wrap the callback function, and the call to the callback function is actually implemented through the delegate, which is also very consistent[Delegated]. So what kind of delegation is? Why can the function name be used as a parameter? Why can it be executed like tg (value? What is the internal implementation mechanism of Delegate. Combine? Next, let's answer them one by one.

Ii. Commission secrets

As mentioned above, the c # compiler implements a lot of processing behind the scenes to simplify code writing. A delegate is indeed usedReference Type of wrapper FunctionWhen we use delegate to define the above delegate, the compiler will generate a class TestDelegate class for us. This class is used to wrap the callback function. You can view the above IL code through ildasm.exe to see the process clearly:

_ Target: object type. When the delegate wraps the instance method, this field references the object of the instance method. If it is a static method, this field is null.

2. _ methodPtr: IntPtr type, an integer used to identify the callback method.

Therefore, for the instance method, the delegate is to call the wrapped method through the instance object. Delegate also exposes two attributes, Target and Method, respectively, representing the metadata of the Instance Object (static Method is null) and the packaging function.

We can see that the class generated after compilation by the compiler has four functions,. Ctor (constructor), BeginInvoke, EndInvoke, Invoke. BeginInvoke/EndInvoke is the asynchronous version of Invoke, So we mainly focus on. ctor and Invoke functions.

The. ctor constructor has two parameters: one object type and one int type. But when we create a new delegate object, passing is the name of a method. In fact, the compiler knows that we want to construct a delegate object, so it will analyze the source code to know which object and method to call; an object reference is used as the first parameter (if it is static, It is null), and the special value used to identify the function from the metadata is used as the second parameter to call the constructor. These two parameters are saved in the _ target and _ methodPth fields respectively.

The Invoke function, as its name implies, is used to call a function. When we execute tg (value), the compiler finds that tg references a delegate object, so the generated code is to call the Invoke method of the delegate object, the signature of this method is consistent with the signature defined in our signature. The generated IL code is: callvirt instance void TestDelegate2.Program/TestDelegate: Invoke (int32 ).

So far, we know that the definition delegate is the definition class, which is used to wrap the callback function. Use the Invoke method of this class to execute the callback function.

Iii. Delegation chain

As mentioned above, all Delegate types will inherit MulticastDelegate. MulticastDelegate indicates a multi-channel broadcast delegate. Its call list can have multiple delegates, which we call a delegation chain. Simply put, it has a delegate list, and we can call all the methods in sequence. The source code shows that MulticastDelegate has a _ invocationList field, which is used to reference an array of Delegate objects. We can use Delegate. combine adds multiple delegates to this array. Since Combine exists, it removes the specified delegate from the delegate chain. Next let's take a look at this specific process. The following code:

            TestDelegate test1 = new TestDelegate(StaticFunction); //1            TestDelegate test2 = new TestDelegate(StaticFunction); //2            TestDelegate test3 = new TestDelegate(new Program().InstanceFunction); //3            TestDelegate result = (TestDelegate)Delegate.Combine(test1, test2); //4            result = (TestDelegate)Delegate.Combine(result, test3); //5            Delegate.Remove(result, test1); //6

When 1 ~ 3 rows, three TestDelegate objects are created, as shown below:

  

When you execute the 4th rows, the system uses Delegate. combine creates a TestDelegate object with a delegate chain. The _ target and _ methodPtr of this object are not what we want to follow. _ invocationList references an array object, and the array has test1, test2. As follows:

  

When you execute the 5th lines of code, a new TestDelegate object with a delegate chain will be created. In this case, _ invocationList has three elements. Note that because Delegate. Combine (or Remove) re-creates the Delegate object every time, the object referenced by the result in row 4th is no longer referenced, and can be recycled. For example:

When you execute Remove, the delegate object will be re-created, similar to Combine. At this time, the test1 delegate object will be removed from the array, which is not repeated here.

Through the above analysis, we know that the calling method is actually to call the Invoke method of the delegate object. If _ invocationList references an array, it will traverse the array and execute all registration methods; otherwise, run the _ methodPtr method. InvokePseudocodeIt may look like the following:

        public void Invoke(Int32 value)        {            Delegate[] delegateSet = _invocationList as Delegate[];            if (delegateSet != null)            {                foreach (var d in delegateSet)                {                    d(value);                }            }            else            {                _methodPtr.Invoke(value);            }        }

_ InvocationList is an internal field, which is called in sequence by default. However, sometimes we want to control this process, such as executing according to certain conditions or recording exceptions. MulticastDelegate has a GetInvocationList () method used to obtain the Delegate [] array. With this array, we can control the specific execution process.

Iv. Generic Delegation

We may use delegation in multiple places. For example, in another assembly, we may define a delegate void AnotherDelegate (int value); the signature of this delegate is the same as that of the signature. In fact, there are many such examples in. net, which we usually see. For example:

            public delegate void WaitCallback(object state);            public delegate void TimerCallback(object state);            public delegate void ParameterizedThreadStart(object obj);

The above is only the form of signature, and there may be a large number of duplicates in the other form, which will bring great difficulty to code maintenance. Generic delegation is designed to solve this problem.

. Net has defined three types of generic delegation:Predicate, Action, Func. These types of parameters are frequently encountered in the method Syntax of using linq.

There are a total of 17 overload actions from no parameter to 16 parameters, which are used to load methods with input values but no return values. For example, delegate void Action <T> (T obj );

Fun has a total of 17 overload values from no parameter to 16 parameters, which are used for the method with input values and return values in the sub-assembly. For example, delegate TResule Func <T> (T obj );

Predicate has only one form: public delegate bool Predicate <T> (T obj ). It is used to encapsulate methods that pass an object and determine whether certain conditions are met. Predicate can also be replaced by Func.

With generic delegation, we do not need to define delegation types everywhere. Unless the requirements are not met, we should give priority to built-in generic delegation.

V. c # Delegate support

5.1 + =/-= Operator

C # The Compiler automatically loads the + = and-= operators for the delegate type to simplify encoding. For example, to add a Delegate object to the Delegate chain, we can also use test1 + = test2; the compiler can understand this writing method, in fact writing and calling test1 = Delegate. the IL code generated by Combine (test1, test2) is the same.

5.2 No need to construct a delegate object

In a place where the delegate object needs to be used, we don't have to create a new one every time, just pass the function to be wrapped. For example, test1 + = StaticFunction; or ExecuteDelegate (StaticFunction, 10); are directly passed functions. The compiler can understand this method, and it will automatically help us create a new delegate object as a parameter.

5.3 you do not need to define the callback method.

Sometimes the callback method only has a few simple lines. We do not want to define a method to make the code more efficient and easy to read. You can use the anonymous method at this time, for example:

ExecuteDelegate (delegate {Console. WriteLine ("using anonymous method") ;}, 10 );

The anonymous method is also modified with the delegate keyword in the form of delegate (parameter) {method body }. The anonymous method is provided by c #2.0, and c #3.0 provides a more elegant lambda expression to replace the anonymous method. For example:

ExecuteDelegate (obj => Console. WriteLine ("use lambda expressions"), 10 );

In fact, the compiler finds that the form parameter of the method is a delegate, while we pass the lambda expression, the compilation will try to randomly generate a special external invisible Method for us, essentially, a new method is defined in the source code. We can see this behavior through The Decompilation tool. Lambda provides a more convenient implementation method, but it is recommended to redefine a method when methods are reused or implemented in a complicated way.

V. Delegation and reflection

Although the Delegate type directly inherits MulticastDelegate, Delegate provides many useful methods. In fact, both of them are abstract classes. You only need to provide one. net Design Issues. Delegate provides two reflection methods: CreateDelegate and DynamicInvoke. CreateDelegate provides multiple overload methods. For details, refer to msdn. The DynamicInvoke parameter number is a variable object array, which ensures that the method can be called when the parameter is unknown. For example:

            MethodInfo methodInfo = typeof(Program).GetMethod("StaticFunction", BindingFlags.Static | BindingFlags.NonPublic);            Delegate funcDelegate = Delegate.CreateDelegate(typeof(Action<int>), methodInfo);            funcDelegate.DynamicInvoke(10);

Here we only need to know the method name (static or instance) and delegate type, and can call the method without knowing the number of parameters, specific types, and return values of the method.

Reflection can bring a lot of flexibility, but efficiency has always been a problem. There are several ways to optimize it. Basically:Delegate. DynamicInvoke, Expression (build Delegate), and Emit. From the above we can see that the DynamicInvoke method still needs to know the specific type of the Delegate (Action <int>), rather than directly constructing the delegate from the MethodInfo metadata of the method. When you know the delegate type, this is the simplest implementation method.

UseDelegate + CacheIt is my favorite method to optimize reflection. Compared with the other two methods, you can consider both efficiency and code readability. You can find the specific implementation method on the Internet, or refer to my Ajax series (coding is not finished yet.

Delegation and events are often associated. Some interviewers also like to ask this question. What is the relationship between them? The next article will discuss the event.

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.