Simplifying LINQ - Extension Methods

by Chad 19. April 2009 11:09

Introduction

Welcome back for part three in my 'Simplifying LINQ' series!  This series of blog posts is intended to simplify the .NET 3.0 language features that make LINQ possible.  I try to do so by showing how the language features have evolved with each framework release.  Hopefully you've been following along, but if not - it's no big deal. If you'd like to catch up you can read about Lambda Expression or Object Initialization, Type Inference, and Anonymous Types.

This post discusses one of my favorite (second only to Generics) .NET language features - Extension Methods.  I'm a fan of extension methods because they enable you to write cleaner more useful code.  With extension methods you can extend the behavior of a class or interface by invoking static methods using instance method syntax.  

I digress...

I'd like to talk briefly about State and Behavior to clarify the limitations of Extension Methods.  State is the instance data that an object encapsulates and behavior is the things that an object does to manipulate it's or other objects' state.  With extension methods you are adding behavior to an object, but cannot add state to that object.  Keep this in mind when learning about extension methods because you may have a natural tendancy to try to add state members.  Additionally, extension methods can only access public members.

For example, if you have an existing Person class with two properties: FirstName and LastName.  You CAN add a GetFullName() method because the person class publicly exposes FirstName and LastName data. You CANNOT add GetDateOfBirth() or SetDateOfBirth(DateTime) methods because the person object doesn't publicly expose a DateTime variable to store that data.

Before .NET 3.0 - Static Methods

Static methods have been around for a long time and you are probably aware of them.  Static methods are nice for utility code because you can call the method without instantiating an instance of the defining class. Static methods can be limiting though because they are syntactically bulky - both to write and to read. For example, the code snippet below uses static methods to increment an integer variable 'i' then convert it to it's textual equivalent.  Notice that the expression is read from the inside out - starting with 'i' then 'Plus1()' then 'ToWord()'. Imagine if this statment were more complex. Nesting more than three or four static method calls would become pretty unmanageable.

string line = IntExtender.ToWord(IntExtender.Plus1(i));

The code for the static methods Plus1(int) and ToWord(int) is below. This code should look familiar and is the basis for extension methods.

public class IntExtender

    {

        public static string ToWord(int value)

        {

            switch (value)

            {

                case 0:

                    return "Zero";

                case 1:

                    return "One";

                case 2:

                    return "Two";

                ...

            }

        }

        public static int Plus1(int value)

        {

            return value + 1;

        }

    }

.NET 3.0 Introduces Extension Methods

With a few minor tweaks to our IntExtender class we can clean up the first statement by making the static methods extension methods. Extension methods use the same syntax as static methods with the addition of two more requirements (which I've emphasized with bolding and underlining.)

 

  • The defining class must be marked static (meaning it can only contain static members)
  • The first argument of the static method must include the 'this' keyword to indicate that that parameter is the type that is being extended. For example, ToWord() and Plus1() extend the 'int' type.

 

    // Extension methods must be defined in a static class

    public static class IntExtender

    {

        // To create an extension method, the first parameter must use the "this"

        // keyword and specify the type that is being extended.

        public static string ToWord(this int value)

        {

            switch (value)

            {

                case 0:

                    return "Zero";

                case 1:

                    return "One";

                case 2:

                    return "Two";

  ;              ...

            }

        }

        public static int Plus1(this int value)

        {

            return value + 1;

        }

    }

Now that we've updated the IntExtender class we can clean up the first statement that we looked at.  The first thing that we need to do is make sure that the namespace that contains the IntExtender class is in scope. You do this with a using statement (Imports in VB.)  If IntExtender is in the same namespace it should already be visible. 

string line = i.Plus1().ToWord();

Notice that the new code using extension methods now reads left to right: 'i' then 'Plus1()' then 'ToWord()'.  With this syntax, you could add many more extension method calls before the code became unreadable.  You should also notice that we do not have to pass in the first 'this' argument to the extension method - the compiler knows to pass in the variable that is being extended.

Final thoughts...

Extension methods are used extensively in LINQ.  The LINQ libraries provide many extension methods to the IEnumerable and IEnumerable<> interfaces. Using a pattern known as method chaining (Extension methods that return the same type that they extend) data can be filtered and manipulated easily using clean syntax such as 'myData.Where(...).OrderBy(...).First(...);'

Extension methods have many uses outside of LINQ. They can be used to extend base class library types, interfaces, and classes that cannot be inherited from. In a future post, I'd like to show some examples of how I've used extension methods to clean up my code. Be sure to play with them and tell me how you've used extension methods in your projects!

Tags: ,

Simplifying-LINQ

Language Enhancements Presentation Materials

by Chad 25. March 2009 18:25

I've uploaded the demo code and slides from my 'Language Enhancements That Make LINQ Possible' presentation at last night's Springfield .NET Users' Group meeting.  Thank you to everyone who attended; I enjoyed the questions and feedback. If you have any problems with the download or questions about the samples, leave me a message in the comments. Enjoy!

SGFUG_LanguageEnhancements.zip (70.19 kb)

Simplifying LINQ: Lambda Expressions

by Chad 21. March 2009 10:53

Using LINQ in your applications has a handful of prerequisites. A programmer confident with LINQ needs to understand Lambda Expressions, Anonymous Types, Extension Methods, Type Inference, Type Initialization, and most likely Expression Trees too.  While this seems like a long list, most of the items are things that you already know, but with a little twist. At the core of LINQ are lambda expressions. Lambda expressions are how you apply your business logic using LINQ constructs. Despite having a cool name, lambda expressions are little more than short-hand for defining a delegate.

Annotated LINQ to SQL Concepts

A delegate is a type that references a method.  Any method that matches the delegate's signature (return type and parameters) can be assigned to it.  Common uses for delegates are for sorting, filtering, or manipulating data. In the .NET framework, delegates are the basis for event handling and have been around since the Framework 1.0 days.  As the .NET Framework has evolved, delegates have remained fundamentally the same, but because of language enhancements and compiler magic the syntax has become something quite useful - Lambda Expressions.

 

Before .NET 2.0 Framework

In the beginning there were Named Methods. Using named methods required a decent amount of code. You had to first define the delegate:

public delegate bool IntFilter(int value);

Then write a method or methods that matched the delegate's signature.

public bool IsEven(int value)

{

    return (value % 2 == 0);

}

Then instantiate and use the delegate.

IntFilter filter = IsEven;

bool match = filter(1); // false

filter = IsOdd;

match = filter(1); // true

This example clearly illustrates the three steps for using delegates. 1.) Define a delegate. 2.) Define a method. 3.) Instantiate the delegate with your method. Where you'll see this most often is in event handling. Below, System.EventHandler is the Delegate and button1_Click is the named method that is assigned to instantiate the EventHandler.

button1.Click += new System.EventHandler(this.button1_Click);

private void button1_Click(object sender, EventArgs e)

{

    // Handle the button click

}

 

Enter the 2.0 Framework

When Microsoft released the .NET 2.0 Framework they introduced some compiler magic called anonymous methods. Anonymous methods allowed you to instantiate a delegate by defining your method inline. Anonymous methods reduce the extra coding effort of writing a separate method when there really isn't a need for it.  

IntFilter filter = delegate(int value) { return (value % 2 == 0); };

I say that this is compiler magic because if you look at your project in a tool like .NET Reflector, you'll see a method defined on your class similar to this.

[CompilerGenerated]

private static bool b__0(int value)

{

    return ((value % 2) == 0);

}

Some subtle but important rules apply to variable scoping in anonymous methods.  Parameters and variables defined within the anonymous method's code block are scoped to the code block. If a variable external to the code block is referenced from within the code block, it is said to be captured.  Captured variables will not be disposed of until the delegate goes out of scope. 

// threshold will NOT be garbage collected until f goes out of scope

int threshold = 5;

IntFilter f = delegate(int value) { return value < threshold; };

 

The 3.0 Framework introduces Lambda Expressions

Lambda Expressions use the lambda operator => (pronounced "goes to") to create delegates or expression trees. Think of them as shorthand for anonymous methods. The syntax is (parameters) => { statements; }. When only one parameter exists the parenthesis are optional. When more than one parameter exists they are separated by commas.

// Func<> and Action<> are two new Generic Delegates that support up to

// four input parameters. Func<> returns a value, Action<> does not.

Func<bool> noParams = () => { return true; };

Func<int, bool> oneParam = (int value) => { return value > 5; };

Func<int, int, bool> twoParams = (int value, int threshold) => { return value > threshold; };

 

  Statement Lambda Expression Lambda
Create Expression Trees No Yes
Instantiate Delegates Yes Yes

I digress...

There are actually two flavors of lambda expressions: Statement Lambdas and Expression Lambdas.  Statement lambdas use curly brackets and contain more than one statement. Expression lambdas do not use curly brackets and contain only one statement.  Statement lambdas cannot be used to create expression trees, expression lambdas can. Both can be used to instantiate delegates. This post discusses statement lambdas. 

Where lambda expressions really become useful is in LINQ.  LINQ provides a set of extension methods to the IEnumerable<> interface that can be used for sorting, filtering, and manipulating the contents of an IEnumerable<>. Below is a graphic with code examples of using the LINQ Where() extension method to filter an array of integers down to just the even integers.  I have highlighted the syntactical differences between using named methods, anonymous methods, and lambda expressions. Hopefully this helps illustrate the convenience that lambda expressions bring to the party and takes away some of the "magic" that can make learning lambda expressions confusing. The examples are ordered chronologically to show how the syntax has evolved to become more concise.

Delegate Syntax Comparisons

As always the best way to learn something is to play with it. To run these samples you'll need a project that targets the .NET 3.5 Framework and you'll need to add a using statement for the System.Linq (in System.Core.dll, Version 3.5) namespace.

Tags: ,

Simplifying-LINQ

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen

About the author

Chad

Meeeee!!!

Hi, my name is Chad Boschert and I'm a software developer in Springfield, Missouri. I've been developing .NET applications in C# since 2002.

Recent Comments

Comment RSS

Calendar

<<  May 2013  >>
MoTuWeThFrSaSu
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar