Lambda expressions are anonymous functions used to create delegates and complex expressions. They can contains expressions-only instructions and complete functions bodies.
In the beginning I must admit that I haven't seen any necessity to use them inside a context like the .NET Framework. I've mostly seen them useful for interpreted languages like Javascript. Using LINQ, I always prefered to use SQL-like sintax against extension methods and lamdba expressions. Instead, during a recent .NET Campus event, I've completely changed my mind. I have been illuminated when I saw lambda expressions applied to several sorting and selecting algorithms in XNA development. They're the best choice for who need to write fast delegates without getting dirty the class code with functions that will be used very rarely.
Now let's code. An example for lambda expressions is:
public void TestLambda() { Thread thread; Button btnButton = new Button(); btnButton.Name = "LambdaTestButton"; thread = new Thread(() => { // Lambda expression Debug.WriteLine("Test lambda expression"); }); thread.Start(); btnButton.Click += new RoutedEventHandler((object sender, RoutedEventArgs e) => { Button btnSender = null; btnSender = sender as Button; Debug.WriteLine(String.Format("Button clicked: {0}" + Environment.NewLine, btnSender.Name)); }); }
Lambda expressions are defined using the operator "=>", that sounds like "goes to". The parenthesis contains the arguments of the lambda expression. Nothing is void, so the function doesn't accept parameters. In the example, a lambda without parameters fits exactly with the ThreadStart delegate of the Thread class second constructor. Just after the "=>" we found the curly brackets which contains the function body, were we coded the output to the debug stream of the string "Test lambda expression". Maybe this is the simplest lambda expression's form that we can have in .NET, but probably the most useful on because it fits most of the useful delegates we have in asynchronous calls, like threads calls and dispatcher calls.
Now lets discuss the Button code. First we must set the Name property to a value, so we can recognize the control later. For the click event handler we coded, this isn't strictly necessary, but we use the Name property for the lambda expression output. Just after the thread, we define a lambda expression for the Click event. The syntax is clearly changed. Inside the parenthesis we have the arguments definition for the RoutedEventHandler delegate, so 'object sender, RoutedEventArgs e' is added to the lambda definition. Inside the curly brackets we have coded the function body just like all the RoundedEventHandler we code. The result is, if we will add the button to a container controller, that we will have the String "Button clicked: LambdaTestButton" on every button click. The event function is valid and visible only for the btnButton object.
Real and practices usage scenarios for a lambda expression?
Now, we can start the funny part, the real usage of a lambda expression. We have seen how a lambda expression let us to place anonymous code inside of our functions. For who actively code in WPF or Silverlight, he sure knows that we have to work with a thread for the UI and various thread for asynchronous computation or working threads. Every long-running operations launched on the UI thread will result in the GUI being freezed until the operation function returns. So we must use the Dispatcher for launch asynchronous operation without blocking the UI. The Dispatcher is a system object used to launch code over a specialized thread and synchronize the result of the operation with the UI thread. One of the most annoying things in development for .NET is to write delegates, one for every method, used to invoke functions inside the dispatcher. One of the worst cases is to write a delegate and a function just only to increase a progress bar value.
Using lambda expression, these scenarios can be solved with only few code lines. Just take a loot at this example:
using System.Linq;
public void PracticalLambda() { String[] stringElements = new String[] { "Latina - LT", "Roma - RM", "Aprilia - LT", "Anzio - RM", "Ariccia - RM", "Sezze - LT", "Pomezia - RM" }; var elements = stringElements.Where(el => el.Contains("RM")); foreach (string selectedElement in elements) Debug.WriteLine(selectedElement + Environment.NewLine); this.Dispatcher.BeginInvoke(() => { var searchelements = stringElements.Where(el => el.Contains("LT")); Debug.WriteLine("Elements from Latina" + Environment.NewLine); foreach (string selectedElement in searchelements) Debug.WriteLine(selectedElement + Environment.NewLine); Debug.WriteLine("Elements from Rome" + Environment.NewLine); foreach (string selectedElement in elements) Debug.WriteLine(selectedElement + Environment.NewLine); }); }
Clearly, in the code we use lambda expressions both for sorting and output the result to the dispatcher. In the first part of the code, we take from the string array only the elements that contains the text "RM". We use the LINQ extension method 'Where()' that compares elements using a user defined comparer. How we write the comparer? Using a lambda expression, clearly. We declare a closure using lambda expression which chooses only the elements that contains the text "RM". When the boolean expression results true, the element is selected. If not, the enumerator steps on to the next one. Just after selecting the elements, we send them to output using a blocking 'foreach' cycle.
The next part of code uses a lambda to invoke over the dispatcher the same search operation and for output the result. Here, we use a very useful trait of the lambda expression. The anonymous functions maintains the visibility scope of the container function. It's cleary visibile from the code that in the second part of the method we output the elements from Rome just using the "elements" variable, defined and set early in the function body.
This trait can be used in the more varied situations. If we need synchronous operation early in the code, then later in our code block we need asynchronous operations, we can use a lambda expression to launch the async code block inside a new Thread. If we also have some special requirements for the parameters that we have to pass to our code, we can use a lambda to launch the code using the same variable scope of our method.
Lambda functions are very powerful.
In the near future I will try to post also sources or even a video tutorial about how we can use lambda in real cases.
Nessun commento:
Posta un commento