Analysis on the principle of implementation of the anonymous function in CLR

zhaozj2021-02-17  48

http://flier_lu.blogone.net/?id=1397624

The implementation principle of the anonymous function in the CLR is analyzed in C # 2.0, and the anonymous function function is implemented via DELEGATE, which can effectively reduce the software of the user, such as

The following is quoted:

...

Button1.click = new eventhandler (Button1_Click);

...

Void Button1_Click (Object Sender, Eventargs E) {

// Do Something, The Button Was ClickED ...

}

...

Can be simplified to use an anonymous function configuration, such as

The following is quoted:

...

Button1.click = delegate (Object sender, Eventargs E) {

// Do Something, The Button Was ClickED ...

}

...

About the use of anonymous functions can refer to the Working With delegates Made Easier With C # 2.0 for Jeffrey Richter. Briefly, the C # compiler will automatically transfer anonymous function code to an automatic naming function, which will automatically complete the user's hand-completed work. For example, construct a private static function, such as

The following is quoted:

Class aclass {

Static void callbackwithOutnewingadeLegateObject () {

Threadpool.queueUserWorkItem (delegate (object obj) {console.writeline (obj);}, 5);

}

}

The compiler is automatically converted to

The following is quoted:

Class aclass {

Static void callbackwithOutnewingadeLegateObject () {

Threadpool.queueUserWorkItem (New Waitcallback (__ AnonymousMethod $ 00000002), 5);

}

Private static void __anonymousmethod $ 00000002 (Object obj) {

Console.writeLine (OBJ);

}

}

The function of automatically generated here is Static, the compiler determines if static decisions are based on the place where this function is used. This is also why the C # 2.0 specification is forbidden to use the GOTO, BREAK and CONTINUE statements from an anonymous method, or jump into it from the outside, because their code is written in a scope, but actually achieves . More convenient is that the compiler can automatically determine the function parameters according to the case where the anonymous function is used, and no user is specified when it is defined.

The following is quoted:

Button1.click = delegate (Object Sender, Eventargs E) {MessageBox.show ("The Button Was Clicked!");

When not using parameters, completely equivalent

The following is quoted:

Button1.Click = delegate {MessageBox.show ("The Button Was Clicked!");

Compared to an anonymous function, the complexity is the use of the anonymous function and its implementation of the variables in its parent. The Grant RI of the MS has a series of discussion articles on its blog. Anonymous Methods, Part 1 of? Anonymous methods, Part 2 of? Anonymous method Part 2 Answers There are two questions that need to be resolved: First, the anonymous function in a variable scope will access the parent function and the class variable; second is anonymous The life cycle of the variable used by the function must be binded to it, and cannot be bound to the parent function. These two problems make the C # compiler to select a more complex independent package to implement anonymous function and the management of the relevant variable lifecycle. First, a local variable in the parent function used by anonymous functions, bored is a reference type or a value type, must be converted from the stack variable to a stack variable to enable the code to access and control the life cycle outside the anonymous function outside its scope. Because the lifecycle of the stack variable is consistent with its owner function, the owner function is exited, and its stack automatically restores the decoupling of the variable lifecycle and the function call lifecycle before the call function. For example, in this simple anonymous function, the local variable of the parent function is used, although this anonymous function is only used in the parent function, but the C # compiler is packaged using the independent class. The following is quoted:

Delegate void delegate1 ();

Public void method1 ()

{

INT i = 0;

Delegate1 D1 = Delegate () {i ;

D1 ();

}

Automatically generated packaging code is similar as follows

The following is quoted:

Delegate void delegate1 ();

Private Sealed Class __localsdisplayClass $ 00000002

{

Public INT I;

Public void __anonymousmethod $ 00000001 ()

{

THIS.I ;

}

}

Public void method1 ()

{

__Localsdisplayclass $ 00000002 local1 = new __localsdisplayclass $ 00000002 ();

Local1.i = 0;

DELEGATE1 D1 = New delegate1 (local1 .__ anonymousmethod $ 00000001);

D1 ();

}

However, for the case of multiple local variables, it is more complicated, such as the code given in the Grant Ri in its example.

The following is quoted:

Delegate void noargs ();

Void someMethod ()

{

Noargs [] methods = new noargs [10];

Int outer = 0;

For (int i = 0; i <10; i )

{

INT inner = i;

Methods [i] = delegate {

Console.writeline ("Outer = {0}", Outer );

Console.writeLine ("i = {0}", i);

Console.writeline ("Inner = {0}", Inner);

}

Methods [i] ();

}

For (int J = 0; j

Methods [J] ();

}

Just a class package variable Outer; a class package variable i; another class package Inner and anonymous function, and reference the first two packages. Because the variables Outer, I and Inner have different scopes, huh, huh. The pseudo code is as follows: The following is a reference:

Private Sealed Class__localsdisplayClass $ 00000008

{

Public int in;

}

Private Sealed Class __localsdisplayClass $ 0000000A

{

Public INT I;

}

Private Sealed Class __localsdisplayclass $ 0000000c

{

Public int inner;

Public __localsdisplayclass $ 00000008 $ locals $ 00000009;

Public __localsdisplayClass $ 0000000A $ locals $ 0000000B;

Public void __anonymousmethod $ 00000007 ()

{

Console.writeline ("Outer = {0}", this. $ Locals $ 00000009.outer );

Console.writeLine ("i = {0}", this. $ Locals $ 0000000b.i);

Console.writeline ("Inner = {0}", this.inner);

}

}

Public void someMethod ()

{

Noargs [] methods = new noargs [10];

__LocalsdisplayClass $ 00000008 local1 = new __localsdisplayclass $ 00000008 ();

Local1.outer = 0;

__Localsdisplayclass $ 0000000a local2 = new __localsdisplayClass $ 0000000A ();

Local2.i = 0;

While (local2.i <10)

{

__Localsdisplayclass $ 0000000c local3 = new __localsdisplayclass $ 0000000c ();

Local3. $ locals $ 00000009 = local1;

Local3. $ locals $ 0000000b = local2;

Local3.inner = local1.i;

Methods [local2.i] = new noargs (local3 .__ anonymousmethod $ 00000007);

Methods [local2.i] ();

}

For (int J = 0; j

Methods [J] ();

}

Summary The law is that each different local variable scope will have a separate class for encapsulation, and if the local variable of the parent field is used in the sub-scope, the package class of the sub-scope references the package class of the parent. The variables and anonymous methods of the same scope are bound by the package class to maintain the consistent lifecycle. Relative to MS is more complex, delphi.net uses a simpler parameter transmission mode for the nested function, because the nested function does not have that complex variable life management requirements, such as

The following is quoted:

Procedure Sayhello;

VAR

Name: String;

Procedure Say;

Begin

Writeln (Name);

END;

Begin

Name: = 'Flier Lu'; SAY;

END;

When the system generates a function SAY code, the upper-level variables used are placed in an automatically generated type ($ unnamed1), and then transmitted to the SAY function as the function parameters, pseudo code similar

The following is quoted:

Type

$ Unnamed1 = record

Name: String;

END;

Procedure @ 1 $ Sayhello $ SAY (VAR UnnamedParam: $ unnamed1);

Begin

Writeln (unnamedparam.name);

END;

Procedure Sayhello;

VAR

Name: String;

Unnamed1: $ unnamed1;

Begin

Name: = 'Flier Lu';

Unnamed1.name: = Name;

Say (unnamed1);

END;

转载请注明原文地址:https://www.9cbs.com/read-29265.html

New Post(0)