01 definition
Lambda expression is actually an unnamed method used to replace a delegate instance
The compiler will convert the lambda Expression into one of the following two: a delegate instance and an Expression tree. The type is Expression, which represents the code in the lambda Expression in the traversable object model. It allows lambda expressions to be interpreted at run time.
example
delegate int Transformer(int i);
Transformer sqr = x => x * x; Console.WriteLine(sqr(3)); //9
In fact, the compiler will parse the Lambda expression by writing a private method, and then move the code of the expression into the method.
02 use
//(parameter) = > expression or statement block (parameters) => expression-or-statement-block
If there is only one parameter and the type can be inferred, the parentheses of the parameter can be omitted
lambda expressions and delegates
- The parameters of each lambda expression correspond to the parameters of the delegate
- The type of the expression corresponds to the return type of the delegate
Statement block
The code of a lambda expression can also be a statement block
x => {return x*x;};
Func and Action delegation
Lambda expressions are typically used with Func and Action delegates
Func<int,int> sqr = x => x*x;
Func<string,string,int> totalLength = (s1,s2) => s1.Length + s2.Length; int tatal = totalLength("hello","world"); // total is 10
Displays the parameter type of the specified lambda expression
Sometimes the parameter type cannot be inferred. At this time, the parameter type of the specified lambda expression should be displayed
void Foo<T> (T x){} void Bar<T> (Action<T> a){}
Bar(x=> Foo(x)); //What type is x?
Bar((int x) => Foo(x)); Bar<int>(x=> Foo(x)); //Specify type Bar<int>(Foo(x)); //Usage group
Capture external variables
lambda expressions can refer to local variables and parameters of the method
static void main(){ int f = 2; Func<int,int> multi = x => x*f; Console.WriteLine(multi(3)); //6 }
In the above example, f is called the captured variable, and lambda expressions that capture external variables are called closures (JavaScript also has the concept of closures)
The captured variables are calculated only when the delegate is actually called, rather than defined. The above example is slightly modified
static void main(){ int f = 2; Func<int,int> multi = x => x*f; f = 10; Console.WriteLine(multi(3)); //The output is 30 }
lambda expressions can also update captured variables
int i = 0; Func<int> add = ()=> i++; Console.WriteLine(add()); //0 Console.WriteLine(add()); //1 Console.WriteLine(i); //2
The life cycle of the captured variable is extended to be the same as that of the delegate
static Func<int> add(){ int i = 0; //This variable is destroyed only after you know that the following lambda expression is not used return ()=> i++; } static void main(){ Func<int> f = add(); Console.WriteLine(f()); //0 Console.WriteLine(f()); //1 }
Local variables within lambda expressions
Local variables instantiated in lambda expressions are unique to each invocation of a delegate instance.
static Func<int> add(){ return () => { int i = 0; return i++; }; } static void main(){ Func<int> f = add(); Console.WriteLine(f()); //0 Console.WriteLine(f()); //0 }
Capture iteration variables
When capturing the iteration variable of the for loop, C# will treat this variable as a variable defined outside the loop, which means that each iteration captures the same variable
Action[] actions = new Action[3]; for(int i=0;i<3;i++){ actions[i] = () => Console.WriteLine(i); } foreach(Action a in actions) a(); //3. Understanding is the same as capturing external variables
How to solve the above problems? Minor change
Action[] actions = new Action[3]; for(int i=0;i<3;i++){ int k = i; //Each lambda expression captures a different k. you can see the scope of k, which is very similar to multithreading actions[i] = () => Console.WriteLine(k); } foreach(Action a in actions) a(); //0 1 2
Note: foreach
Difference between C#4 and C#5 +
Action[] actions = new Action[3]; int i=0; foreach(char c in 'abc'){ actions[i++] = () => Console.WriteLine(c); } foreach(Action a in actions) a(); //The C#4 output is ccc and the C#5 + output is abc
lambda expressions vs native methods
Native methods are a new feature of C#7.
There are many functional repetitions between it and Lambda expression, but it has three advantages:
• recursion can be done simply and clearly
• there is no need to specify the delegate type (that pile of code)
• slightly lower performance overhead
The local method is more efficient because it avoids indirect calls of delegates (CPU cycles, memory allocation). Local methods can also access the local variables of the method, and there is no need for the compiler to hoist the captured variables to hidden classes.
Anonymous method vs Lambda expression
Anonymous methods are similar to Lambda expressions, but lack the following three features:
- Implicit type parameter
- Expression syntax (only statement blocks)
- The ability to compile the expression tree by assigning values to Expressions
example
delegate int Add(int i);
Add add = delegate (int x){return x*x}; //Anonymous Methods Console.WriteLine(add(3)); //9
Add add = x => x*x; //Lambda expression
Therefore, anonymous methods are gradually not used