Exploring function creation and closure in c #
Understanding the advantages of the closure in c # when creating an anonymous function in a dynamic way, most developers have used it more or less. Review the evolution of dynamic function creation in c #: C #1.0: public delegate string DynamicFunction (string name); public static DynamicFunction GetDynamicFunction () {return GetName ;} static string GetName (string name) {return name;} var result = GetDynamicFunction () ("mushroom"); 3.0 it seems cumbersome and lagging behind. When I just learned to delegate, I understood the delegate as a function pointer. Also, let's look at the function pointer implementation: char GetName (char p); typedef char (* DynamicFunction) (char p ); dynamicFunction GetDynamicFunction () {return GetName;} char GetName (char p) {return p ;}; char result = GetDynamicFunction () ('M '); in comparison, c #1.0 is almost the same (reference/pointer difference). After all, it is in the same family. Add an anonymous function in C #2.0: public delegate string DynamicFunction (string name); DynamicFunction result2 = delegate (string name) {return name ;}; www. bkjia. added the Lambda expression in comC #3.0 and turned around: public static Func <string, string> GetDynamicFunction () {return name => name;} var result = GetDynamicFunction () ("mushroom"); while adding Lambda expressions to the limitations of anonymous functions has greatly simplified our workload. But there are indeed some shortcomings: var result = name => name; these errors are reported during compilation. Because c # is a strong-type language, the var syntax sugar is provided to save the workload of declaring and determining the type. The compiler must be able to completely deduce the types of parameters during compilation. The name parameter type in the Code cannot be inferred during compilation. Var result = (string name) => name; Func <string, string> result2 = (string name) => name; Expression <Func <string, string> result3 = (string name) => name; the name type is directly declared above. Unfortunately, an error is returned. The answer is provided in the Code. The compiler cannot infer whether the right Expression belongs to the Func <string, string> type or Expression <Func <string, string> type. Dynamic result = name => name; dynamic result1 = (Func <string, string>) (name => name); If dynamic is used, the compiler cannot tell whether the right is a delegate, we can convert it. Func <string, string> function = name => name; DynamicFunction df = function; a func delegate is defined here, although the parameter and return value types are the same as the DynamicFunction delegate, however, an error is reported during compilation: you cannot implicitly convert Func <string, string> to DynamicFunction. The two types are incompatible. Understanding the closures in c # involves the dynamic creation of functions. There is a lot of information about the concept of closure. The theoretical part will not be repeated here. Let's take A look at the closure in c # code: Func <int> A = () => {var age = 18; return () ==>// function B {return age ;};}; var result = A (); The above is the closure, which can be understood as: Cross-scope access to the variable in the function, there are also behaviors with data. C # There are three types of variable scopes: class variables, instance variables, and function variables. Variables in the sub-scope accessing the parent scope (that is, accessing the instance/class variables in the function) are taken for granted by us and conform to our programming habits. In this example, anonymous function B is the variable age that can access upper-layer function. For the compiler, function A is the parent scope of function B, so the age variable accessed by function B to the parent scope is compliant with the specification. Int age = 16; void Display () {Console. writeLine (age); int age = 18; Console. writeLine (age);} The above compilation reports an error and is not declared for use. After the compiler checks that the age is declared in the function, the scope will overwrite the age of the parent scope (like JS undefined ). Func <int> C = () => {var age = 19; return age ;}; the class functions C, then function A cannot access the age variable in Function C. In short, variables in other functions cannot be accessed across scopes. Then how does the compiler implement the closure mechanism? For example, the answer is to upgrade the scope of A function to an instance class scope. During code compilation, the compiler automatically generates an anonymous Class x when checking that function B uses the variable in function, the age variable in the original function A is upgraded to the field of the x class (that is, the instance variable), and the function is upgraded to the instance function of the anonymous class x. The following code is generated by the compiler (simplified): class Program1 {static Func <int> CachedAnonymousMethodDelegate2; static void Main (string [] args) {Func <int> func = new Func <int> (Program1. B); int num = func ();} static Func <int> B () {DisplayClass cl = new DisplayClass (); cl. age = 18; return new Func <int> (cl. a) ;}} sealed class DisplayClass {public int age; public int A () {return this. age ;}} let's take a look at the complex example: sta Tic Func <int, int> GetClosureFunction () {int val = 10; Func <int, int> interAdd = x => x + val; Console. writeLine (interAdd (10); val = 30; Console. writeLine (interAdd (10); return interAdd;} Console. writeLine (GetClosureFunction () (30); the output values are 20, 40, and 60. When we see that the val variable in this function is passed through the closure, we know that val is not just a variable in the function. We have previously analyzed how the compiler generates code, knowing that val is an instance variable of an anonymous class, And interAdd is an instance function of an anonymous class. Therefore, no matter how many layers are passed by val, its value is always maintained until it leaves the (chained) scope. There are many discussions about closures in js. Likewise, function A () {var age = 18; return function () {return age ;}} can be compared and understood ;}} A (); the advantages of closures protect variables. If you want to expose a variable value, but you are afraid that the declared class or instance variable will be contaminated by other functions, you can design a closure and use it only through function calls. Logical continuity and variable persistence. A () is to execute part of the logic. A () only follows the () logic. During the logical context, variables are always maintained and can be used at will.