C# 3.0 Language Enhancements for LINQ – Part 1

10 04 2008

LINQ is a new feature added to C# 3.0. For an introduction to LINQ, please visit here

So, how are we able to use LINQ seamlessly with C#? Thanks to some of the enhancements that were made to C# for LINQ. They are as follows,

1) Lambda Expressions

2) The var Keyword

3) Extension Methods

4) Partial Methods

5) Expression Trees

This is the start of 3 part series which would be,

· Part 1 – Lambda Expressions

· Part 2 – The var Keyword, Extension Methods and Partial Methods

· Part 3 – Expression Trees

As said above, this post will be explaining about Lambda Expressions

Lambda Expressions

Lambda Expression is an anonymous function that can contain an expression or statement or can also be used to create a delegate.

To understand Lambda Expressions, it is better to first look into,

1) Named Methods

2) Anonymous Methods

Named Methods

With delegates one can create Named Methods, that is, instantiate a named method to a delegate and invoke it

image

Nothing gets explained better without an example; so, let’s get into our sample application

Sample Application

We are going to create a sample application which is going to filter names that contains a character. It’s a very basic and simple example, but it’s really good to explain the things that we want to explore. We would be seeing on how we approach the same concept using Named Methods and Anonymous Methods and then come to Lambda Expressions.

We have a small class called Utility which looks like this,

clip_image004

It has got two static methods and a delegate called CustomFilter with an input parameter as string and bool as return type. So, the idea is to allow the developer or user to write their own custom filter function, but use this Utility to filter. This is done with the help of delegates.

Using Named Methods

So, if we take our Named Method approach, we would end up doing like this,

static void UsingNamedMethods()

{

List<string> FilteredNames =

Utility.FilterNames(names, MyFilter);

foreach (string name in FilteredNames)

Console.WriteLine(name);

}

static bool MyFilter(string name)

{

if (name.Contains(‘c’))

return true;

return false;

}

We have a method called MyFilter which is where we define our custom filter logic and instantiate the delegate CustomFilter with this method using the Utility.FilterNames function

And here is our Utility.FilterNames function

public delegate bool CustomFilter(string name);

public static List<string>

FilterNames(string[] names,CustomFilter customFilter)

{

List<string> NamesList = new List<string>();

foreach(string name in names)

{

if (customFilter(name))

NamesList.Add(name);

}

return NamesList;

}

It’s fairly simple and straight forward. Now we really have something useful which makes use of Named Methods. This sample will help the Customer who is going to use our Utility to write his own Filter and use the generic FilterNames function to filter it.

Look into our filter method which is Myfilter – It’s fairly simple, just checking whether the name contains a character and returns true if so. Do we really need to write a method for this? Why can’t we specify a code block instead and make use of it? Anonymous Methods comes to our rescue!

Anonymous Methods

Anonymous methods can be used to pass a code block to a delegate, using the delegate parameter, and can be used in places where creating a method is really not necessary, like in our example.

Using the Anonymous method, our code changes to,

static void UsingAnonymousMethods()

{

List<string> FilteredNames;

FilteredNames =

Utility.FilterNames(names,

delegate(string name)

{

return (name.Contains(‘c’));

} );

foreach (string name in FilteredNames)

Console.WriteLine(name);

}

So, now we have a code block which checks for a character in the name and it is passed as our CustomFilter delegate

Note that our FilterNames Utility function remains unchanged.

However, Anonymous methods do have one drawback in regard to readability. It’s more verbose and the code block sometimes becomes really hard to read!

So, do we really have anything which is easy to read and also simple to use? – Yes, and Lambda Expressions comes to our rescue!

Revisiting Lambda Expressions

As I said earlier – Lambda Expression is an anonymous function that can contain an expression or statement or can also be used to create a delegate

The structure of a lambda expression looks like this,

(param1,param2..paramN)=>expression

Basically we have some input parameters delimited with comma on the left and a corresponding expression on the right

The simplest lambda expression is,

x=>x

This is nothing but assigning x to x

Moving forward, our sample’s expression to find a character in the name would now become,

n=>n.Contains(‘c’)

How is the type of ‘n’ inferred? Remember our delegate? Here it is again,

public delegate bool CustomFilter(string name);

If you translate in pure English – Here is a delegate called CustomFilter which accepts one input parameter of type string and returns a value of type bool

So, when we actually make use of lambda expressions, this gets inferred and thus our ‘n’ in the above lambda expression gets inferred that it is of type string and has to return bool. But when inferring types is not possible, you could always do,

(string n)=>n.Contains(‘c’)

This would explicitly specify the type of ‘n’

Coming back to our sample application, now with the use of lambda expressions, we could write,

static void UsingLambdaExpressions()

{

List<string> FilteredNames;

FilteredNames = Utility.FilterNames(names, n => n.Contains(‘c’));

foreach (string name in FilteredNames)

Console.WriteLine(name);

}

Note that our FilterNames Utility function still remains unchanged.

Statement Lambdas

Again, revisiting our lambda expression definition – Lambda Expression is an anonymous function that can contain an expression or statement or can also be used to create a delegate

We did see that lambda expression that has an expression. What about a statement?

Statement lambdas look like,

(param1,param2..paramN)=>{ statement; }

The statement body can contain many statements instead of a single expression. Something like,

FilteredNames =

Utility.FilterNames(

names,

n =>{

bool blnVal;

/*… something here …*/

blnVal = n.Contains(‘c’);

return blnVal;

} );

Lambdas with Func<T,TResult> delegates

Again, revisiting our lambda expression definition – Lambda Expression is an anonymous function that can contain an expression or statement or can also be used to create a delegate

We did see that lambda expression that has an expression, a statement. What about the last one which can be used to create a delegate?

With the Func<T,TResult> family of generic delegates, we can use lambda expressions to create a delegate and invoke it.

Before getting into an example, let us explore the Func<T,TResult> family. We currently have,

image

A Simple example would be,

Func<string, bool> SampleFunction = n =>n.Contains(‘c’);

SampleFunction(“Chirstchurch”);

The above code block looks fairly simple and actually we see that we can eliminate Named Methods and Anonymous Methods now and directly make use of lambda expressions!

Coming back to our example, we can now simplify our FilterNames Utility function to something like this,

public static List<string> FilterNamesUsingFuncDelegates

(string[] names,Func<string,bool> customFilter)

{

List<string> NamesList = new List<string>();

foreach (string name in names)

{

if (customFilter(name))

NamesList.Add(name);

}

return NamesList;

}

Yes, you are right; we no need to declare any delegate now and make use of the available function delegates as Predicates!

Predicates were introduced in .NET 2.0 and are defined as (from MSDN),

Represents the method that defines a set of criteria and determines whether the specified object meets those criteria

You can read more about Predicates here

We can come across the same in LINQ when you use Standard Query Operators. If you remember our LINQ example, we have actually seen this,

IEnumerable<string> query = names

.Where(n => n.Equals(matchName))

.Select();

And Where clause syntax is,

clip_image008

You can download the sample application here

Useful Resources

1) Named Methods

2) Anonymous Methods

3) Standard Query Operators