3.1 Anonymous method expressions
An anonymous method expression (anonymous-method-expression) defines an anonymous method (anonymous) and obtains a special value that references the method.
anonymous method parameters:
parameter modifier Optional type identifier
anonymous method expression (anonymous-method-expression) is a value that complies with a special conversion rule (see 3.3). This value has no type, but can be implicitly converted to a compatible delegate type.
anonymous method Expressions (anonymous-method-expression) define a new declaration space for parameters, local variables and constants, and labels.
3.2 Anonymous method signature
The optional anonymous method signature (anonymous-method-signature) defines the name and type for the formal parameters of an anonymous method. These parameters act as blocks of anonymous methods. If the scope of a local variable contains an anonymous method expression (anonymous-method-expression), and the parameter of the anonymous method is the same as the local variable, a compilation error is generated.
If an anonymous method expression (anonymous-method-expression) has an anonymous method signature (anonymous-method-signature), the compatible delegate type is coerced to have the same parameter type and modifier. and have the same order (see 3.3). If an anonymous method expression (anonymous-method-expression) does not have an anonymous method signature (anonymous-method-signature), the compatible delegate type is forced to require no out parameters.
Note the anonymous method signature (anonymous-method-signature) cannot contain an attribute or an array of arguments (to implement a variable-length argument list). However, an anonymous method signature (anonymous-method-signature) can be compatible with a delegate type that contains a parameter array.
3.3 Anonymous Method conversions
An anonymous method expression (anonymous-method-expression) is a special value that has no type. An anonymous method expression (anonymous-method-expression) can be used to delegate build expressions (delegate-creation-expression) (see 3.3.1). Other valid applications for anonymous method expressions (anonymous-method-expression) depend on the implicit conversions that are defined on them.
An implicit conversion exists between an anonymous method expression (Anonymous-method-expressio) and any compatible delegate type. If D is a delegate type, and a is an anonymous method expression (anonymous-method-expression), D and a are compatible only if the following two conditions are true.
First, the parameter type of D must be compatible with a:
If a does not contain an anonymous method signature (anonymous-method-signature), D can have 0 or more parameters of any type, but these parameters cannot have an out modifier.
If you have an anonymous method signature (anonymous-method-signature), d must have the same number of arguments as a, and each parameter in a must have the same type as the corresponding parameter in D. and the ref or out modifier on each parameter in a must be the same as the corresponding parameter in D. If the last parameter in D is a parameter array, there is no compatible A and D.
Second, the return value type of D must be compatible with a. Because of this rule, a cannot contain blocks of other anonymous methods.
If the return value type of D is declared void, then all returns statements contained in a are unable to specify an expression.
If the return value type of D is declared as R, then all returns statements contained in a are not allowed to specify an expression that can be implicitly converted to R. The end point of block in a must be up to.
In addition to implicit conversions between compatible delegate types, there is no conversion between an anonymous method expression (anonymous-method-expression) and any type, including the object type.
The following examples explain these rules in detail:
delegate void D (int x);
D D1 = delegate {}; That's right
D D2 = Delegate () {}; Error, Signature mismatch
D D3 = delegate (long x) {}; Error, Signature mismatch
D D4 = delegate (int x) {}; That's right
D d5 = delegate (int x) {return;}; That's right
D d6 = delegate (int x) {return x;}; Error, the return value does not match
delegate void E (out int x);
E e1 = delegate {}; Error, E with an output parameter
E e2 = delegate (out int x) {x = 1;}; That's right
E E3 = delegate (ref int x) {x = 1;}; Error, Signature mismatch
delegate int P (params int[] a);
P P1 = delegate {}; Error, the end of block is not up to
P P2 = delegate {return;}; Error, return value type mismatch
P P3 = delegate {return 1;}; That's right
P P4 = delegate {return "Hello";}; Error, return value type mismatch
P P5 = delegate (int[] a) {//correct
return a[0];
};
P P6 = delegate (params int[] a) {//error, (extra) params modifier
return a[0];
};
P P7 = Delegate (int[] a) {//error, return value type mismatch
if (A.length > 0) return a[0];
return "Hello";
};
Delegate Object Q (params int[] a);
Q q1 = delegate (int[] a) {//correct
if (A.length > 0) return a[0];
return "Hello";
};
3.3.1 Delegate-Build expression
A delegate-building expression (delegate-creation-expression) can be used as a substitute for the conversion syntax between anonymous methods and delegate types. If the parameter expression (expression) of a delegate-building expression (delegate-creation-expression) is an anonymous method expression (anonymous-method-expression), The anonymous method is converted to the given delegate type according to the implicit conversion rule defined above. For example, if D is a delegate type, the expression
New D (Delegate {Console.WriteLine ("Hello");})
Equivalent to an expression
(D) Delegate {Console.WriteLine ("Hello");}
3.4 Anonymous method blocks
The block of an anonymous method expression (Anonymous-method-expression) complies with the following rules:
If the anonymous method contains a signature, the parameters specified in the signature are available in blocks (block). If the anonymous method does not contain a signature, it can be converted to a delegate type with parameters (see 3.3), but these parameters are inaccessible in blocks (block).
Unless a ref or out parameter is specified in the signature (if any) of the most appropriate anonymous method, a compile-time error occurs when a ref or out parameter is accessed in a block.
When this type of this is a struct type, accessing this in blocks (Block) is a compilation error, whether the access is explicit (such as this.x) or implicit (such as x, and X is an instance method of the struct). This rule simply prohibits such access, and thus does not affect the lookup results of the members of the struct.
Block can access variables outside of the anonymous method (see 3.5). Access to an external variable is referenced to an instance of the variable that should be active in the evaluation of an anonymous method expression (anonymous-method-expression) (see 3.6).
A compilation error occurs if the target of a goto statement, break statement, or continue statement contained in a block is either outside of block or an anonymous method contained in block.
Return statements in blocks (block) will control returns from a call to the most recent anonymous method, rather than from a function member. The expression specified in the Return statement must match the delegate type obtained by the anonymous method expression (anonymous-method-expression) conversion (see 3.3).
In addition to evaluating and invoking anonymous method expressions (anonymous-method-expression), there are no explicit restrictions on how blocks are executed. In particular, the compiler chooses to implement an anonymous method by synthesizing one or more named methods or types. These synthetic names must remain in the space used by the compiler: These names must contain two consecutive underscores.
3.5 External variables
If an anonymous method expression (anonymous-method-expression) is contained in the scope of any local variable, value parameter, or parameter array, it is said to refer to those local variables, A value parameter or an array of parameters) is an external variable (outer variable) of the anonymous method expression (anonymous-method-expression). In an instance function member of a class, this is considered a value parameter and is an external variable of any anonymous method expression (anonymous-method-expression) contained in the function member.
3.5.1 Capture External variables
When an external variable is referenced in an anonymous method, the external variable is said to be captured by an anonymous method. Typically, the lifetime of a local variable is the end point of the execution of the block or the statement with which it is associated. However, the lifetime of a captured external variable lasts until the delegate that references the anonymous method meets the garbage collection criteria.
In the following example:
Using System;
delegate int D ();
Class Test {
Static D F () {
int x = 0;
D result = delegate {return ++x;}
return result;
}
static void Main () {
D d = F ();
Console.WriteLine (d ());
Console.WriteLine (d ());
Console.WriteLine (d ());
}
}
The local variable x is captured by an anonymous method, and the lifetime of X is extended at least until the delegate returned from F conforms to the garbage collection condition (this does not necessarily occur at the end of the program). Since the invocation of each anonymous method operates the same instance of X, the output of this example will be:
1
2
3
When a local variable or a value parameter is captured by an anonymous method, the local variable learns that the parameter will no longer be considered a fixed variable, but is considered a movable variable. Therefore, if any unsafe code records the address of the external variable, you must first use the fixed statement to fix the variable.
The instantiation of 3.5.2 Local variables
When a program executes into the scope of a variable, the local variable is instantiated. For example, when the following method is invoked, the local variable x is instantiated and initialized three times-each time the loop iterates.
static void F () {
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
...
}
}
However, moving the declaration of X to the outside of a loop only causes an instantiation of x:
static void F () {
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
...
}
}
In general, there is no way to observe how many times a local variable has been instantiated-because the lifetime of the instantiation is disjointed, and it is possible for each instantiation to simply use the same storage space. However, when an anonymous method captures a local variable, the effect of the instantiation is obvious. The following example
Using System;
delegate void D ();
Class Test {
Static d[] F () {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
Result[i] = delegate {Console.WriteLine (x);};
}
return result;
}
static void Main () {
foreach (d d in F ()) D ();
}
}
The output is:
1
3
5
However, when the declaration of X is moved outside the loop:
Static d[] F () {
D[] result = new D[3];
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
Result[i] = delegate {Console.WriteLine (x);};
}
return result;
}
The results are:
5
5
5
Note that according to the contract operation (see 3.7), the three delegates established by the above version of the F method are equal. In addition, the compiler can, but does not have to, optimize these three instances into a single delegate instance (see 3.6).
Anonymous method delegates can share some of the captured variables and have separate instances of other capture variables. For example, if f changes to
Static d[] F () {
D[] result = new D[3];
int x = 0;
for (int i = 0; i < 3; i++) {
int y = 0;
Result[i] = delegate {Console.WriteLine ("{0} {1}", ++x, ++y);
}
return result;
}
These three delegates capture the same (one) instance of X and capture a separate instance of Y. The output is:
1 1
2 1
3 1
A separate anonymous method can capture the same instance of an external variable. In the following example:
Using System;
delegate void Setter (int value);
delegate int Getter ();
Class Test {
static void Main () {
int x = 0;
Setter s = delegate (int value) {x = value;};
Getter g = delegate {return x;};
S (5);
Console.WriteLine (g ());
S (10);
Console.WriteLine (g ());
}
}
Two anonymous methods capture the same instance of local variable x, which can be "communicated" by altering the amount. The output of this example is:
5
10
3.6 Anonymous Method evaluation
The evaluation of an anonymous method expression at run time will get a delegate instance that references this anonymous method and a set of external variables for a group of activities (possibly empty). When a delegate evaluated by an anonymous method expression is invoked, the anonymous method body is executed. The code in the method body can use a captured set of external variables that are referenced by the delegate.
The invocation list of a delegate that has an anonymous method expression contains a unique entry point. The exact target object and the target method of the delegate are undefined. In particular, whether the target object of a delegate is null, the this value inside a function member, or another object is undefined.
The evaluation of the semantically consistent anonymous method expression for an external variable with the same set of (possibly empty) captures can, but does not have to, return the same delegate instance. The term "semantically consistent" here means that the execution of anonymous methods in any case should have the same effect on the same parameters. This rule allows you to optimize your code as follows:
Delegate double Function (double x);
Class Test {
Static double[] Apply (double[] A, Function f) {
Double[] result = new Double[a.length];
for (int i = 0; i < a.length i++) result[i] = f (a[i));
return result;
}
static void F (double[] A, double[] b) {
A = Apply (A, delegate (double x) {return Math.sin (x);});
b = Apply (b, delegate (double y) {return Math.sin (y);});
...
}
}
Because two anonymous method delegates have the same set of captured external variables (empty), and the anonymous method is semantically consistent, the compiler can make the two delegates refer to one of the same target methods. Even for these two anonymous method expressions, the compiler can return exactly the same delegate instance.
3.7 Delegate instance Equality
The following rules determine the result of an anonymous method delegate instance's contract operator and Object.Equals method:
A delegate instance that is semantically identical to an anonymous method expression (anonymous-method-expressions) with the same set (and possibly none) of the captured variable can be (but not necessarily) equal.
An anonymous method expression (anonymous-method-expressions) that is different from the semantically identical form capture variable must have unequal delegate instances.
3.8 Definite assigned value
Explicit assignment rules for anonymous method parameters and named methods (named method, distinct from anonymous methods) are the same as explicitly reproduced by parameters. That is, reference parameters and value parameters must be initialized with a state-specific assignment, and output parameters can be no more definitely assigned than the specified values.
Also, if you want to use an output parameter before the anonymous method returns to normal, the output parameter must be definitely assigned.
When control is transferred to the block of an anonymous method expression, the definite assignment for an external variable v is the same as the definite assignment of V before an anonymous method expression.
The definite assignment rule for the variable v after an anonymous method is the same as the explicit assignment rule before the anonymous method expression.
The following example:
delegate bool Filter (int i);
void F () {
int Max;
Error, Max is not definitely assigned
Filter f = delegate (int n) {return n < max;}
max = 5;
DoWork (f);
}
Generates a compilation error because Max was not definitely assigned before the anonymous method declaration. The following example
delegate void D ();
void F () {
int n;
D d = delegate {n = 1;};
D ();
Error, n is not definitely assigned
Console.WriteLine (n);
}
Also produces a mutation error because the assignment of n in an anonymous method does not affect the explicit assignment of N to an anonymous method.
3.9 Method Group Conversions
Similar to the anonymous methods described in section 3.3, an implicit conversion exists between a method group and a compatible delegate type.
For a given method Group E and a delegate type D, an implicit conversion of E to D exists if a delegate that is a form new D (e) is allowed to establish an expression (see 2.9.6).
The following example:
Using System;
Using System.Windows.Forms;
Class Alertdialog {
Label message = new label ();
Button OKButton = New button ();
Button CancelButton = New button (); '
Public Alertdialog () {
Okbutton.click + = new EventHandler (Okclick);
Cancelbutton.click + = new EventHandler (Cancelclick);
...
}
The constructor uses two new operators to establish two delegate instances. Implicit method group conversions allow them to be written in a shorter form:
As with other implicit and explicit conversions, conversion operators can be used explicitly in a particular transformation. Therefore, the following example:
Object obj = new EventHandler (Mydialog.okclick);
Can write:
Object obj = (EventHandler) Mydialog.okclick;
Method groups and anonymous method expressions affect overloaded choices, but do not participate in type inference. For more details see section 2.6.4
3.10 Implementation Instance
This section discusses the possible implementations of anonymous methods in some standard C # constructs. The implementation described here is based on the same principle as the Visual C # compiler, but it is not necessarily an implementation, but a possibility.
The end of this section will give a code instance that contains an anonymous method with different characteristics. For each example, the corresponding code that is converted uses only the constructs provided by Standard C #. In these examples, assume that identifier D is a delegate type such as the following:
public delegate void D ();
The simplest method of anonymity is an anonymous method that does not capture any external variables:
Class Test {
static void F () {
D d = delegate {Console.WriteLine ("Test");};
}
}
It is translated into a delegate instance that references a static method generated by a compiler that contains the code in the anonymous method:
Class Test {
static void F () {
D d = new D (__METHOD1);
}
In the following example, the anonymous method references instance members of this:
In the following example, your name method references the instance member of this:
Class Test {
int x;
void F () {
D d = Delegate {Console.WriteLine (x);};
}
}
This can is translated to a compiler generated instance method containing the code of the anonymous method:
It is translated as a compiler-generated instance method that contains the code in the anonymous method:
Class Test {
int x;
void F () {
D d = new D (__METHOD1);
}
void __method1 () {
Console.WriteLine (x);
}
}
In this example, the anonymous method captures a local variable:
In this example, the anonymous method captures a local variable:
Class Test {
void F () {
int y = 123;
D d = delegate {Console.WriteLine (y);};
}
}
The lifetime of the local variable must now is extended to in least the lifetime of the anonymous method delegate. This can is achieved by "lifting" to the local variable into a field of a compiler generated class. Instantiation of the local variable (§21.5.2) then corresponds to creating a instance of the compiler class, an D Accessing the local variable corresponds to accessing a field in the instance of the compiler class. Furthermore, the anonymous method becomes a instance method of the compiler class:
The lifetime of a local variable must now extend to at least the end of the lifetime of the anonymous method delegate. This can be done by "elevating" the local variable to the domain of a compiler-generated class. The instantiation of a local variable (see 3.5.2) is equivalent to creating an instance of a compiler-generated class, and accessing a local variable is equivalent to accessing the domain of this instance of the class created by the compiler. In addition, the anonymous method becomes an instance method of the compiler-generated class:
Class Test {
void F () {
__LOCALS1 = new __LOCALS1 ();
__LOCALS1.Y = 123;
D d = new D (__LOCALS1.__METHOD1);
}
Class __locals1 {
public int y;
public void __method1 () {
Console.WriteLine (y);
}
}
}
Finally, the following anonymous method captures this as a two local variables with different lifetimes:
Finally, the following anonymous method captures this and two local variables with different lifetimes:
Class Test {
int x;
void F () {
int y = 123;
for (int i = 0; i < i++) {
int z = i * 2;
D d = Delegate {Console.WriteLine (x + y + z);};
}
}
}
Here, a compiler generated class are created for each statement blocks in which locals are captured such this locals in The different blocks can have independent lifetimes. An instance of __LOCALS2, the compiler generated class for the inner statement blocks, contains the local variable z and a field that references an instance of __LOCALS1. An instance of __LOCALS1, the compiler generated class for the outer statement blocks, contains the local variable y and a field that is references this is the enclosing function member. With this data structures it is possible to reach all captured outer variables through a instance of __local2, and the C Ode of the anonymous method can thus is implemented as a instance method of that class.
Here, the compiler generates a class for each statement block that captures a local variable. Therefore, those local variables captured in different blocks have independent lifetimes. An instance of the class __LOCALS2 created by the compiler for an inner statement block, containing a local variable z and a domain referencing an instance of __LOCALS1. An instance of the class __LOCALS1 created by the compiler for the outer statement block, containing a local variable y and a field referencing this of the class that contains the function member. With these data structures, you can obtain all the captured external variables through an instance of __LOCALS2, and the code in the anonymous method is thus implemented as an instance method of the class.
Class Test {
void F () {
__LOCALS1 = new __LOCALS1 ();
__locals1.__this = this;
__LOCALS1.Y = 123;
for (int i = 0; i < i++) {
__LOCALS2 = new __locals2 ();
__LOCALS2.__LOCALS1 = __LOCALS1;
__LOCALS2.Z = i * 2;
D d = new D (__LOCALS2.__METHOD1);
}
}
Class __locals1 {
Public Test __this;
public int y;
}
Class __locals2 {
Public __LOCALS1 __locals1;
public int z;
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.