An analysis of the implementation principle of anonymous functions in CLR

Source: Internet
Author: User
Tags anonymous functions implement variables variable scope wrapper
The realization principle of anonymous function in function CLR

C # 2.0 provides the ability to implement anonymous functions via delegate, which effectively reduces the user's thin-code work, such as


The following are references:

...
Button1. Click + + new EventHandler (button1_click);
...
void button1_click (Object sender, EventArgs e) {
Do something, the button is clicked ...
}
...




can be simplified to construct directly using anonymous functions, such as


The following are references:

...
Button1. Click + = delegate (Object sender, EventArgs e) {
Do something, the button is clicked ...
}
...




The use of anonymous functions can refer to the Jeffrey Richter working with delegates Made easier with C # 2.0. In short, the C # compiler automatically transfers anonymous function code to an automatic named function, which automatically completes the work that originally required the user to do it manually. For example, construct a private static function, such as


The following are references:

Class AClass {
static void Callbackwithoutnewingadelegateobject () {
ThreadPool.QueueUserWorkItem (delegate (Object obj) {Console.WriteLine (obj);}, 5);
}
}




is automatically converted by the compiler to


The following are references:

Class AClass {
static void Callbackwithoutnewingadelegateobject () {
ThreadPool.QueueUserWorkItem (New WaitCallback (__anonymousmethod$00000002), 5);
}

private static void __anonymousmethod$00000002 (Object obj) {
Console.WriteLine (obj);
}
}




The automatically generated function here is static, and the compiler determines whether or not the function is static based on where it is used. This is why the C # 2.0 specification prohibits the use of Goto, break, and continue statements to jump out of or out of an anonymous method, because their code, though written in a scope, is not actually implemented together.
It is more convenient for the compiler to automatically determine function parameters based on the use of anonymous functions, without the user being specified at the time of definition, as


The following are references:

Button1. Click + = Delegate (Object sender, EventArgs e) {MessageBox.Show ("the button was clicked!");




When parameters are not used, they are completely equivalent to


The following are references:

Button1. Click + = delegate {MessageBox.Show ("the button was clicked!");





Compared to the implementation of anonymous functions, it is more complex to use and implement anonymous functions for variables in their parent scope. Ms's Grant RI has a series of discussion articles on its blog.
Anonymous Methods, Part 1?
Anonymous Methods, part 2?
Anonymous Method Part 2 answers

There are two problems to solve: one is that anonymous functions that are not in the scope of a variable can access the variables of the parent function and the class, and that the life cycle of the variable that the anonymous function uses must be bound to it, not to the invocation lifecycle of the parent function. These two problems enable the C # compiler to choose the more complex independent class encapsulation way to realize the management of anonymous function and related variable life cycle.

First, the anonymous function uses local variables in the parent function, whether boredom is a reference type or a value type, and must be converted from a stack variable to a heap variable so that the anonymous function implementation code outside its scope can access and control the lifecycle. Because the lifecycle of a stack variable is consistent with its owner function, the owner function exits and its stack is automatically restored to the calling function before it can be decoupled from the life cycle of the variable and the function call.
For example, in the following simple anonymous function, the local variable of the parent function is used, although the anonymous function is used only in the parent function, but the C # compiler uses a standalone class to wrap the variables it uses.


The following are references:

delegate void Delegate1 ();

public void Method1 ()
{
int i=0;

Delegate1 D1 = delegate () {i++;};

D1 ();
}




The automatically generated wrapper code is similar to the following


The following are references:

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, it is more complicated to have multiple local variable scopes, such as the code given by Grant RI in its example


The following are references:

delegate void Noargs ();

void SomeMethod ()
{
Noargs [] methods = new NOARGS[10];
int outer = 0;
for (int i = 0; i < 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. Length; J + +)
METHODS[J] ();
}




You need a class wrapper variable outer; One class encapsulates the variable I; another class encapsulates inner and anonymous functions, and references instances of the previous two encapsulated classes. Because the variable outer, I and inner have different scopes, hehe. Pseudo code is as follows:


The following are references:

Private Sealed class __localsdisplayclass$00000008
{
public int outer;

};
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. Length; J + +)
METHODS[J] ();
}





To sum up its rule is that each different local variable scope will have a separate class to encapsulate, if the child scope uses to the parent scope local variable, then the child scope's encapsulation class refers to the parent scope's encapsulation class. Variables of the same scope and anonymous methods are bound together by the wrapper class to maintain their consistent lifecycle.

Compared to MS's more complex implementations, Delphi.NET uses simpler parameter-passing methods for nested functions because nested functions do not have the complexity of variable lifecycle management requirements, such as


The following are references:

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 ancestor variable that is used, such as name, is put into an auto-generated type ($Unnamed 1), and then passed as a function parameter to the say function, which is similar to the pseudo code


The following are references:

Type
$Unnamed 1 = Record
name:string;
End

Procedure @1$sayhello$say (Var unnamedparam: $Unnamed 1);
Begin
Writeln (Unnamedparam.name);
End

Procedure SayHello;
Var
name:string;
Unnamed1: $Unnamed 1;
Begin
Name: = ' flier Lu ';

Unnamed1.name: = Name;

Say (Unnamed1);
End



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.