|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
[The sample code requires the May 2006 LINQ Preview to be installed in order to work.]
IntroductionThis article will introduce you to lambda expressions and expression trees – two new related features coming up in the newest version of C# and the .NET runtime. You will learn how to create them, and how to use them to enhance and simplify your C# code. Knowledge of the concepts behind delegates in the .NET framework is assumed. Let's start by brushing up on anonymous methods, since the concepts behind them will help in understanding lambda expressions. Anonymous Methods.NET 2.0 introduced a new construct: anonymous methods. Instead of declaring a named method in your class and then referencing the method by name when creating a delegate: bool MatchNumbersBelow10(int n)
{
return n<10;
}
...
int GetNumber(List<int> numbers)
{
//gets the first number smaller than 10 in the list
return numbers.Find(MatchNumbersBelow10);
}
...you can write the method directly where it is used: int GetNumber(List<int> numbers)
{
//gets the first number smaller than 10 in the list
return numbers.Find(
delegate(int n)
{
return n<10;
}
);
}
As you can see, in the above sample we are passing in a special kind of nameless inline method as a delegate directly to the Anonymous Method RulesThe rules for defining an anonymous method are simple:
Lambda ExpressionsC# 3.0 and the .NET 3.0 Runtime introduce a more powerful construct that builds on the anonymous method concept. It allows you to pass an inline expression as a delegate, with minimal syntax. Instead of the anonymous method we declared above: delegate (int n)
{
return n<10;
}
...we can do: n => n<10
It looks shorter and more concise, doesn't it? But how does it work? The basic form for a lambda expression is:
In the example above, we have an argument named //gets the first number smaller than 10 in the list
int result=numbers.Find( n=> n<10);
To understand better how the lambda expression syntax differs from the anonymous method syntax, let's turn our example anonymous method: delegate(int n)
{
return n<10;
}
...into its lambda-expression equivalent: n=> n<10
We don't need the (int n)
{
return n<10;
}
Let's replace the braces with a (int n) => return n<10;
The (int n)=> n<10
Now, that's already a usable lambda expression - but we can simplify it just a bit more. The type of the argument can be inferred as well by the compiler, so we can remove the type declaration for the argument. (n)=> n<10
We can also take out the parenthesis now, because we don't give the types of the arguments. n=> n<10
And there's our final lambda expression! As you can probably see just by that example, the big advantage of lambda expressions in normal coding is that the syntax is more readable and less verbose. This becomes quickly more important the more complex code becomes. For example, when we just add one more argument, take a look at the difference between the length and readability of an anonymous method vs. a lambda expression: //anonymous method
numbers.Sort(delegate(int x, int y){ return y-x; });
//lambda expression
numbers.Sort((x,y)=> y-x);
And in a more complex example, with multiple delegate properties, compare anonymous methods: ControlTrigger trigger= new ControlTrigger ();
trigger.When=delegate(Control c, ThemePart t)
{
return c.Enabled && c.MouseOver;
};
trigger.Action=delegate(Control c, ThemePart t)
{
t.Visible=true;
};
trigger.ExitAction=delegate(Control c, ThemePart t)
{
t.Visible=false;
};
...with lambda expressions: ControlTrigger trigger=new ControlTrigger();
trigger.When=(c,t)=> c.Enabled && c.MouseOver;
trigger.Action=(c,t)=> t.Visible=true;
trigger.ExitAction=(c,t)=> t.Visible=false;
Features and RulesReturn type and name cannot be specified explicitly (just as with anonymous methods). The return type is always inferred from the delegate signature, and there is no need for a name since the expression is always handled as a delegate. You can omit parentheses for the argument list if the expression has one argument: n => n<10
...unless its argument has an explicitly-declared data type: //Type explicitly declared for an argument - have to include parentheses!
(string name)=> "Name: " + name
If the expression has more than one argument or has no arguments, you must include the parentheses. A lambda expression doesn't have to return a value if the signature of the delegate it is being cast to has a return type of delegate void EmptyDelegate();
…
EmptyDelegate dlgt= ()=> Console.WriteLine("Lambda without return type!");
The code used in a lambda doesn't have to be a single statement. You can include multiple statements if you enclose them inside a statement block: Action<Control> action=
control=>
{
control.ForeColor=Color.DarkRed;
control.BackColor=Color.MistyRose;
});
In this form, the lambda more closely resembles an anonymous method, but with a less verbose syntax. Lambda statement blocks are not supported by the VS IDE in the LINQ Preview, so they will be underlined as a syntax error. However, they will compile and run correctly in spite of the IDE's lack of support. You can access local variables and arguments in the outer method from within the expression, just as you can do with anonymous methods. void GetMatchesFromList(List<int> matchValues)
{
List<int> numbers=GetNumbers(); //Get a list of numbers to search in.
//Get the first number in the numbers list that is also contained in the
//matchValues list.
int result=numbers.Find(n=> matchValues.Contains(n));
}
UsesLambda expressions are nifty anywhere you need to pass a little bit of custom code to a component or method. Where anonymous methods were useful in C# 2.0, lambda expressions really shine in C# 3.0. Some examples are expressions for filtering, sorting, iterating, converting, and searching lists (using the useful methods introduced in .NET 2.0): List<int> numbers=GetNumbers();
//find the first number in the list that is below 10
int match=numbers.Find(n=> n<10);
//print all the numbers in the list to the console
numbers.ForEach(n=> Console.WriteLine(n));
//convert all the numbers in the list to floating-point values
List<float> floatNumbers=numbers.ConvertAll<float>(n=> (float)n);
//sort the numbers in reverse order
numbers.Sort((x, y) => y-x);
//filter out all odd numbers
numbers.RemoveAll(n=> n%2!=0);
...progress update handlers passed to a long-running method: metafileConverter.Convert(filename,
percentComplete=> progressBar.Value=percentComplete);
...and simple event handlers: slider.ValueChanged+= (sender, e)=> label.Text=slider.Value.ToString();
XLinq, MS's new technology for querying XML documents, and Linq, MS's new technology for querying object collections, use lambda expressions heavily. How to Use Lambda ExpressionsWhen the upcoming version of .NET is released, using lambda expressions will be as simple as declaring them in the places you would normally use anonymous methods or delegates: //passing a method as a delegate
int match=numbers.Find(MatchNumbersUnder10);
//passing an anonymous method as a delegate
int match=numbers.Find(delegate(int n) { return n<10; });
//passing a lambda expression as a delegate
int match=numbers.Find(n=> n<10);
Currently, though, you must take some additional steps. The LINQ Preview must be installed on your machine, and you must create a project using one of the LINQ project templates so that VS will know to use the C# 3 compiler included with the LINQ Preview installation. Lambda Expression TreesThere's another powerful feature of lambda expressions that is not obvious at first glance. Lambda expressions can be used as expression trees (hierarchies of objects defining the components of an expression – operators, property access sub-expressions, etc) instead of being directly turned to code. This way, the expressions can be analyzed at runtime. To make a lambda expression be treated as an expression tree, assign or cast it to the type Expression<Predicate<int>> expression = n=> n<10;
The expression tree created by the expression defined above looks like this:
As you can see, This expression tree can also be created manually like this: Expression<Predicate<int>> expression = Expression.Lambda<Predicate<int>>(
Expression.LT(
Expression.Parameter(typeof(int), "n"),
Expression.Constant(10)
),
Expression.Parameter(typeof(int), "n")
);
An expression tree can be compiled and turned into a delegate using the //Get a compiled version of the expression, wrapped in a delegate
Predicate<int> predicate=expression.Compile();
//use the compiled expression
bool isMatch=predicate(8); //isMatch will be set to true
The UsesAs shown above, the properties of the expression tree objects can be used to get detailed information about all parts of the expression. This information can be used to translate the expression into another form, extract dependency information, or do other useful things. Microsoft's Database Language-Integrated Query (DLinq) technology, to be introduced in the upcoming version of the .NET Runtime, is based on translation of lambda expressions to SQL at runtime. I am working on a component that will allow you to do simple automatic binding via expressions, like this): Binding binding=new Binding<Entry, Label>();
binding.SourceObject=src;
binding.DestObject=dest;
binding.SourceExpression=(src,dest)=> src.Name +
" – added on "+src.Date.ToString();
binding.DestExpression=(dest)=>dest.Text;
binding.Bind();
The Another usage would be a dynamic filter that automatically keeps a list control up-to-date based on changes to the text entered in a filter textbox. All you would have to do to set up the dynamic filter would be: listControl.Filter=(item)=> item.Name.StartsWith(textBox.Text);
Whenever Another thing that expression trees are potentially useful for is lightweight dynamic code generation. You can build an expression tree manually as I described above, then call ConclusionAs you can see, lambda expressions and expression trees open a lot of new possibilities! I am excited to work with them and to see the uses other developers think of for them.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||