The story behind it

Source: Internet
Author: User

[Turn] The Story Behind and the story behind

Happy Lambda expressions (2)

Lambda has brought us a lot of joy since its appearance with. NET Framework3.5 in front of. NET developers. It is elegant, more friendly to developers, and can improve development efficiency. Oh, my God! It may also reduce the possibility of some potential errors. Many of the functions of ASP. net mvc in LINQ are implemented using Lambda. I can only say that since Lambda is used, my waist is no longer sour, my legs do not hurt, and my fingers do not crud, I will not even write code bugs. Dear friends, have you used Lambda today? But do you really understand it? Let's get to know each other today.

This article will introduce some basic knowledge about Lambda, and then we will have a small performance test to compare the performance of Lambda expressions and common methods. Then we will use IL to learn more about what Lambda is, finally, we will use Lambda expressions to implement some common patterns in JavaScript.

  • Learn about Lambda
  • Performance of Lambda expressions
  • Use Lambda expressions to implement some popular JavaScript patterns
    • Callback Mode
    • Return Method
    • Custom Method
    • Self-execution Method
    • Object instant Initialization
    • Runtime Branch
  • Summary
Learn about Lambda

In. NET 1.0, we all know that we often use delegation. With delegation, we can transfer methods like passing variables. In a certain program, delegation is a powerful type of managed method pointer, which was also widely used at the moment, however, in general, delegated use is still cumbersome. Take the following steps to use a delegate:

Complicated? Okay, maybe it's not complicated in, but now it's really complicated.

Later, we were lucky that. NET 2.0 brought generics. So we have generic classes and generic methods, and more importantly, generic delegation. In the end, at. NET3.5, our Microsoft colleagues finally realized that we only need two generic delegation (with heavy load) to cover 99% of the application scenarios.

  • Action does not have a wildcard delegate that inputs parameters and returned values.
  • Action <T1 ,..., T16> can receive one to 16 non-return value generic delegation parameters.
  • Func <T1 ,..., T16, Tout> can receive a wildcard delegate with 0 to 16 parameters and return values

In this way, we can skip the first step. However, step 1 is required, but it is replaced by Action or Func. Don't forget that we still have the anonymous method in. NET2.0. Although it is not popular, we also give it a chance to show its face.

Func<double, double> square = delegate (double x) {return x * x;}

Finally, it was our turn to make Lambda debut elegantly.

// The Compiler does not know what is next, so we cannot use the var keyword Action dummyLambda = () => {Console. WriteLine ("Hello World from a Lambda expression! ") ;}; // Double y = square (25); Func <double, double> square = x => x * x; // double z = product (9, 5); Func <double, double, double> product = (x, y) => x * y; // printProduct (9, 5); Action <double, double> printProduct = (x, y) => {Console. writeLine (x * y) ;}; // var sum = dotProduct (new double [] {1, 2, 3}, new double [] {4, 5, 6}); Func <double [], double [], double> dotProduct = (x, y) => {var dim = Mat H. Min (x. Length, y. Length); var sum = 0.0; for (var I = 0; I! = Dim; I ++) sum + = x [I] + y [I]; return sum ;}; // var result = matrixVectorProductAsync (...); func <double, double, Task <double> matrixVectorProductAsync = async (x, y) =>{ var sum = 0.0;/* do some stuff using await... */return sum ;};

 

From the code above, we can see that:

  • If there is only one parameter, no need to write ()
  • If there is only one execution statement and we want to return it, no {} is required and no return is required.
  • Lambda can be executed asynchronously, as long as the async keyword is added to the front.
  • The Var keyword cannot be used in most cases.

Of course, for the last one, we can still use the var keyword in the following cases. The reason is simple. Let's tell the compiler what type will be followed.

Func<double,double> square = (double x) => x * x;Func<string,int> stringLengthSquare = (string s) => s.Length * s.Length;Action<decimal,string> squareAndOutput = (decimal x, string s) =>{    var sqz = x * x;    Console.WriteLine("Information by {0}: the square of {1} is {2}.", s, x, sqz);};

Now, we know some basic usage of Lambda. If it is just these things, it is not a happy Lambda expression. Let's take a look at the following code.

var a = 5;Func<int,int> multiplyWith = x => x * a;var result1 = multiplyWith(10); //50a = 10;var result2 = multiplyWith(10); //100

Is there a feeling? We can use external variables in Lambda expressions. That's right, that is, the legendary closure.

void DoSomeStuff(){    var coeff = 10;    Func<int,int> compute = x => coeff * x;    Action modifier = () =>    {        coeff = 5;    };    var result1 = DoMoreStuff(compute);    ModifyStuff(modifier);    var result2 = DoMoreStuff(compute);}int DoMoreStuff(Func<int,int> computer){    return computer(5);}void ModifyStuff(Action modifier){    modifier();}

In the code above, the coeff variable in the DoSomeStuff method is actually modified by the external method ModifyStuff,That is to sayModifyStuffThis method has access to DoSomeStuffAbility of a local variable. How is it done? J. Of course, this variable scope problem should also be paid attention to when using closures. A slight carelessness may lead to unexpected consequences. Look at the following and you will know.

var buttons = new Button[10];for (var i = 0; i < buttons.Length; i++){    var button = new Button();    button.Text = (i + 1) + ". Button - Click for Index!";    button.OnClick += (s, e) => { Messagebox.Show(i.ToString()); };    buttons[i] = button;}

Guess what the result is when you click these buttons? Yes "1, 2, 3 ...". However, the actual results are all 10. Why? Don't you see it? So what if we want to avoid this situation?

var button = new Button();var index = i;button.Text = (i + 1) + ". Button - Click for Index!";button.OnClick += (s, e) => { Messagebox.Show(index.ToString()); };buttons[i] = button;

In fact, the method is very simple. It is to save the current I in the for loop, so the values stored in each expression are different.

Next, we will focus on advanced products and expressions that are closely related to Lambda ). The reason is closely related, because we can use an Expression to save a Lambda. This Lambda expression can be interpreted at runtime. Let's look at the following simple code:

Expression<Func<MyModel, int>> expr = model => model.MyProperty;var member = expr.Body as MemberExpression;var propertyName = member.Expression.Member.Name; 

This is indeed one of the simplest expressions. We use expr to store the following expressions. The compiler will generate an Expression Tree for us. The expression tree contains a metadata like the parameter type, name, and method body. In linq to SQL, we use this method TO pass the conditions we set through the where Extension Method TO the subsequent LINQ Provider for explanation, the process interpreted by the LINQ Provider is actually the process of converting the expression tree into an SQL statement.

Performance of Lambda expressions

Regarding Lambda performance, we may first ask if it is faster than a common method? Or slow? Next, let's look at it. First, we use a piece of code to test the performance difference between common methods and Lambda expressions.

class StandardBenchmark : Benchmark{    const int LENGTH = 100000;    static double[] A;    static double[] B;    static void Init()    {        var r = new Random();        A = new double[LENGTH];        B = new double[LENGTH];        for (var i = 0; i < LENGTH; i++)        {            A[i] = r.NextDouble();            B[i] = r.NextDouble();        }    }    static long LambdaBenchmark()    {        Func<double> Perform = () =>        {            var sum = 0.0;            for (var i = 0; i < LENGTH; i++)                sum += A[i] * B[i];            return sum;        };        var iterations = new double[100];        var timing = new Stopwatch();        timing.Start();        for (var j = 0; j < iterations.Length; j++)            iterations[j] = Perform();        timing.Stop();        Console.WriteLine("Time for Lambda-Benchmark: \t {0}ms", timing.ElapsedMilliseconds);        return timing.ElapsedMilliseconds;    }    static long NormalBenchmark()    {        var iterations = new double[100];        var timing = new Stopwatch();        timing.Start();        for (var j = 0; j < iterations.Length; j++)            iterations[j] = NormalPerform();        timing.Stop();        Console.WriteLine("Time for Normal-Benchmark: \t {0}ms", timing.ElapsedMilliseconds);        return timing.ElapsedMilliseconds;    }    static double NormalPerform()    {        var sum = 0.0;        for (var i = 0; i < LENGTH; i++)            sum += A[i] * B[i];        return sum;    }}}

The code is very simple. We can compare it by executing the same code. One is placed in a Lambda expression, and the other is placed in a common method. The following results are obtained after four tests:

Lambda Normal-Method

70 ms 84 ms
73 ms 69 ms
92 ms 71 ms
87 ms 74 ms

Lambda should be a little slower than a normal method, but I don't know why Lambda is faster than a normal method for the first time. --! However, through such a comparison, I would like to explain at least that there is almost no difference in the performance between Lambda and common methods.

What Will Lambda become after compilation? Let LINQPad tell you.

The Lambda expression in is as follows:

Action<string> DoSomethingLambda = (s) =>{Console.WriteLine(s);// + local};

The common method is written as follows:

void DoSomethingNormal(string s){Console.WriteLine(s);}

What about the IL code generated by the above two sections of code? Yes:

DoSomethingNormal:IL_0000:  nop         IL_0001:  ldarg.1     IL_0002:  call        System.Console.WriteLineIL_0007:  nop         IL_0008:  ret         <Main>b__0:IL_0000:  nop         IL_0001:  ldarg.0     IL_0002:  call        System.Console.WriteLineIL_0007:  nop         IL_0008:  ret       

The biggest difference is the method name and method usage rather than Declaration. The Declaration is actually the same. Through the above IL code, we can see that this expression is actually put in the current class by the compiler. So in fact, it is no different from the method in our call class. The following figure shows the compilation process:

External variables are not used in the code above. Let's look at another example.

void Main(){int local = 5;Action<string> DoSomethingLambda = (s) => {Console.WriteLine(s + local);};global = local;DoSomethingLambda("Test 1");DoSomethingNormal("Test 2");}int global;void DoSomethingNormal(string s){Console.WriteLine(s + global);}

What is the difference between this IL code?

IL_0000:  newobj      UserQuery+<>c__DisplayClass1..ctorIL_0005:  stloc.1     IL_0006:  nop         IL_0007:  ldloc.1     IL_0008:  ldc.i4.5    IL_0009:  stfld       UserQuery+<>c__DisplayClass1.localIL_000E:  ldloc.1     IL_000F:  ldftn       UserQuery+<>c__DisplayClass1.<Main>b__0IL_0015:  newobj      System.Action<System.String>..ctorIL_001A:  stloc.0     IL_001B:  ldarg.0     IL_001C:  ldloc.1     IL_001D:  ldfld       UserQuery+<>c__DisplayClass1.localIL_0022:  stfld       UserQuery.globalIL_0027:  ldloc.0     IL_0028:  ldstr       "Test 1"IL_002D:  callvirt    System.Action<System.String>.InvokeIL_0032:  nop         IL_0033:  ldarg.0     IL_0034:  ldstr       "Test 2"IL_0039:  call        UserQuery.DoSomethingNormalIL_003E:  nop         DoSomethingNormal:IL_0000:  nop         IL_0001:  ldarg.1     IL_0002:  ldarg.0     IL_0003:  ldfld       UserQuery.globalIL_0008:  box         System.Int32IL_000D:  call        System.String.ConcatIL_0012:  call        System.Console.WriteLineIL_0017:  nop         IL_0018:  ret         <>c__DisplayClass1.<Main>b__0:IL_0000:  nop         IL_0001:  ldarg.1     IL_0002:  ldarg.0     IL_0003:  ldfld       UserQuery+<>c__DisplayClass1.localIL_0008:  box         System.Int32IL_000D:  call        System.String.ConcatIL_0012:  call        System.Console.WriteLineIL_0017:  nop         IL_0018:  ret         <>c__DisplayClass1..ctor:IL_0000:  ldarg.0     IL_0001:  call        System.Object..ctorIL_0006:  ret      

Have you found out? The content compiled by the two methods is the same. DoSomtingNormal and <> c _ DisplayClass1. <Main> B _ 0 have the same content. But the biggest difference, please note.When external variables are used in Our Lambda expressions, the compiler will generate a class for this Lambda expression, which contains our expression methods.Where the Lambda expression is used, it is actually called by an instance of the new class. In this case, the external variables in our expression, that is, the local used in the code above, are actually stored in this instance as a global variable.

Use Lambda expressions to implement some popular patterns in JavaScript

Speaking of JavaScript, it has been a breeze in recent years. Not only can we apply some existing design patterns in our software engineering, but also some patterns generated by JavaScript features due to its flexibility. For example, modularization and immediate execution of method bodies .. NET is a strongly compiled language, and its flexibility is naturally inferior to that of JavaScript, but this does not mean that JavaScript can do things. NET cannot do. Below we will implement some interesting JavaScript writing.

Callback Mode

The callback mode is not exclusive to JavaScript. In fact, in. NET1.0, we can use a delegate to implement callback. But the callback we want to implement today is different.

void CreateTextBox(){var tb = new TextBox();tb.IsReadOnly = true;tb.Text = "Please wait ...";DoSomeStuff(() => {tb.Text = string.Empty;tb.IsReadOnly = false;});}void DoSomeStuff(Action callback){// Do some stuff - asynchronous would be helpful ...callback();}

In the above Code, we will do something after DoSomeStuff is complete. This method is very common in JavaScript. Isn't oncompleted and onsuccess of Ajax in jQuery implemented in this way? Or is foreach not the same in the LINQ extension method?

Return Method

We can directly return a method in JavaScript. Although the method cannot be directly returned in. net, we can return an expression.

Func<string, string> SayMyName(string language){switch(language.ToLower()){case "fr":return name => {return "Je m'appelle " + name + ".";};case "de":return name => {return "Mein Name ist " + name + ".";};default:return name => {return "My name is " + name + ".";};}}void Main(){var lang = "de";//Get language - e.g. by current OS settingsvar smn = SayMyName(lang);var name = Console.ReadLine();var sentence = smn(name);Console.WriteLine(sentence);}

Is there a sense of strategy? This is not perfect. The heap of switch cases is so upset that we can use Dictionary <TKey, TValue> to simplify it. It seems that this product:

static class Translations{static readonly Dictionary<string, Func<string, string>> smnFunctions = new Dictionary<string, Func<string, string>>();static Translations(){smnFunctions.Add("fr", name => "Je m'appelle " + name + ".");smnFunctions.Add("de", name => "Mein Name ist " + name + ".");smnFunctions.Add("en", name => "My name is " + name + ".");}public static Func<string, string> GetSayMyName(string language){//Check if the language is available has been omitted on purposereturn smnFunctions[language];}}
Custom Method

Custom methods are common in JavaScript. The main implementation idea is that the method is set as an attribute. When attaching a value to this property or even executing it, we can change the point of this property at any time to achieve the goal of changing this method.

Class SomeClass {public Func <int> NextPrime {get; private set;} int prime; public SomeClass {NextPrime = () =>{ prime = 2; NextPrime = () => {
// The logic code for executing NextPrive () after the second and second times can be added here: return prime ;}; return prime ;}}}

In the above Code, when NextPrime was called for the first time, it was 2. At the same time, we changed NextPrime. We can point it to another method, which is not inferior to the flexibility of JavaScrtip? If you are not satisfied, the following code should satisfy you.

Action <int> loopBody = I => {if (I = 1000) loopBody = // point the loopBody to another method/* The first 10000 executions of the following code */}; for (int j = 0; j <10000000; j ++) loopBody (j );

We don't need to think too much about the call, and then the method itself has tuning. Our original practice may be to write the corresponding code after I = 1000. What is the difference between this method and the current method pointing to another method?

Self-execution Method

The self-execution method in JavaScript has the following advantages:

In C #, we can also use self-executed methods:

(() => {// Do Something here!})();

The preceding example does not contain parameters. If you want to add parameters, it is also very simple:

((string s, int no) => {// Do Something here!})("Example", 8);

. NET4.5 what are the most transient new features? Async? You can also

Await (async (string s, int no) =>{// use the Task to asynchronously execute the code here}) ("Example", 8 ); // code after asynchronous Task execution
Object instant Initialization

We all know that. NET provides us with anonymous objects, which can be used to create the desired objects as freely as in JavaScript. But don't forget, in JavaScript, you can not only put data, but also put methods. NET, can you? Believe that Microsoft will not disappoint us.

//Create anonymous objectvar person = new {Name = "Jesse",Age = 28,Ask = (string question) => {Console.WriteLine("The answer to `" + question + "` is certainly 42!");}};//Execute functionperson.Ask("Why are you doing this?");

However, if you really run this code, an exception will be thrown. The problem is that Lambda expressions cannot be assigned to anonymous objects. However, the delegation is acceptable, so here we only need to tell the compiler what type of delegation I am.

var person = new {Name = "Florian",Age = 28,Ask = (Action<string>)((string question) => {Console.WriteLine("The answer to `" + question + "` is certainly 42!");})};

But there is another problem here. If I want to access a certain property of person in the Ask method, can I?

var person = new{                Name = "Jesse",                Age = 18,                Ask = ((Action<string>)((string question) => {                    Console.WriteLine("The answer to '" + question + "' is certainly 20. My age is " + person.Age );                }))};

The result is that even compilation is not successful, because the person is not defined in Our Lambda expression. Of course, it cannot be used, but there is no problem in JavaScript. What should I do ?. NET? Of course, since it needs to be defined in advance, we can define it in advance.

dynamic person = null;person = new {Name = "Jesse",Age = 28,Ask = (Action<string>)((string question) => {Console.WriteLine("The answer to `" + question + "` is certainly 42! My age is " + person.Age + ".");})};//Execute functionperson.Ask("Why are you doing this?");  
Runtime Branch

This mode is a bit similar to a custom method. The only difference is that it is not defining itself, but other methods. Of course, this implementation is possible only when this method is defined based on attributes.

public Action AutoSave { get; private set; }public void ReadSettings(Settings settings){/* Read some settings of the user */if(settings.EnableAutoSave)AutoSave = () => { /* Perform Auto Save */ };elseAutoSave = () => { }; //Just do nothing!}

Some people may think this is nothing, but think about it. You only need to call AutoSave outside, and you don't need to worry about anything else. This AutoSave does not need to check the configuration file every time it is executed.

Summary

Lambda expressions are actually a method after the final compilation, and we declare that Lambda expressions are actually transmitted in the form of delegation. Of course, we can also pass through the generic Expression. Using Lambda expressions to form a closure can do a lot of things, but there are still some usage disputes. This article is just an overview :). If there is anything wrong, please make a brick. Thank you for your support :)

For more new ways to use Lambda expressions, see the story behind-happy Lambda expressions (2)

Link: http://www.codeproject.com/Articles/507985/Way-to-Lambda

Related Article

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.