Extension Methods Part 2 – Lambda Expressions in Linq

 

 

In my previous entry on this topic I demonstrated how you can use Extension methods to add what appear to be new methods to existing classes. In this entry I will demonstrate how Extension methods enhance Linq by adding “method queries” using lambda expressions.

To see how this works, let’s set up a simple query,  with a display that will show the two ways we might execute that query using Linq.

We’ll start by creating a new Silverlight application and within the application a simple data class, Person. Here is the code for Person.cs

using System.Collections.Generic;

namespace ExtensionPart2
{
   public class Person
   {
      public string FirstName { get; set; }
      public string LastName { get; set; }
      public string EmailAddress { get; set; }

      public override string ToString()
      {
         return string.Format( "{0} {1}\n{2}",
                     FirstName, LastName, EmailAddress );
      }

      public static List<Person> GetPeople()
      {
         List<Person> people = new List<Person>();
         people.Add( new Person() 
         { 
            FirstName = "Jesse", 
            LastName = "Liberty", 
            EmailAddress = "jliberty@microsoft.com" 
         } );

         people.Add( new Person() 
         { 
            FirstName = "George", 
            LastName = "Washington", 
            EmailAddress = "gWashington@whitehouse.gov" 
         } );



// also added John Adams, Thomas Jefferson
// James Madison, James Monroe, John Q. Adams return people; } } }

(I’ve elided the initialization of 5 of the presidents to save room)

 

We will issue two queries against this data. The first is a traditional Linq query,

from person in people
where person.LastName.StartsWith( "M" )
select person;

While this works fine, taking advantage of extension methods allows us (or more accurately the authors of the library) to extend the List<t> class to add the Where method, which in turn allows us to collapse these three statements to a single line of code using a lambda expression,

people.Where( person => person.LastName.StartsWith( "M" ) );

Examining the Extension Method

The tooltip for people.Where give great insight into what is actually going on here

whereExtension

Taking this apart, we see that Where is defined to be an extension method that returns an IEnumerable<Person> and takes one argument: a function (named predicate) that takes two arguments: a Person object and a boolean.

Rather than passing in a method, or a delegate to a method, or even an anonymous method, we go one step further and pass in a lambda expression. As you know from previous articles, the lambda expression “are a way to write short in-line substitutions for the methods that delegates refer to.”

You can read the lambda expression above as

“There is a method that takes one parameter, a list of person objects named people and that returns  each person whose last name starts with the letter m in an IEnumberable collection.”

or, more conventionally you can read it as

“person goes to each person whose last name starts with m”

For a lengthy review of how to read this aloud see this article.

The result for both queries is identical, but I would argue the second query is far more readable, as shown in the running program,

Extension2Running

The code for Page.xaml is shown here, and below it the code for Page.xaml.cs

Page.xaml

<UserControl x:Class="ExtensionPart2.Page"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d"
  HorizontalAlignment="Left"
  VerticalAlignment="Center"
  FontFamily="Georgia"
  FontSize="14"
  Width="650"
  Height="553"
  Margin="5">
  <Grid x:Name="LayoutRoot"
        Background="Bisque"
        Margin="20">
    <Grid.RowDefinitions>
      <RowDefinition Height="1*" />
      <RowDefinition Height="5*" />
      <RowDefinition Height="3*" />
      <RowDefinition Height="3*" />
      <RowDefinition Height="1.5*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="6*" />
      <ColumnDefinition Width="4.5*" />
    </Grid.ColumnDefinitions>
    <TextBlock Height="Auto"
               Width="Auto"
               Grid.Row="0"
               Text="Where last name begins with M"
               TextWrapping="Wrap"
               FontFamily="Georgia"
               FontSize="24"
               Margin="0"
               Grid.RowSpan="1"
               Grid.ColumnSpan="2"
               HorizontalAlignment="Center" />
    <TextBlock Text="Presidents"
               TextWrapping="NoWrap"
               FontFamily="Georgia"
               FontSize="18"
               HorizontalAlignment="Left"
               VerticalAlignment="Top"
               Margin="5,5,5,5"
               d:LayoutOverrides="Height"
               Grid.Row="1" />
    <ListBox x:Name="PresidentsListBox"
             Margin="5"
             Grid.Row="1"
             Grid.Column="1" />
    <TextBlock HorizontalAlignment="Left"
               VerticalAlignment="Bottom"
               Margin="5,4,5,5"
               Grid.Row="2"
               FontFamily="Georgia"
               FontSize="14"
               Text=""
               TextWrapping="Wrap" />
    <ListBox x:Name="LinqResults"
             Margin="5"
             Grid.Row="2"
             Grid.RowSpan="1"
             Grid.Column="1" />
    <ListBox x:Name="QOEM_Results"
             Margin="5"
             Grid.Column="1"
             Grid.Row="3"
             Grid.RowSpan="1" />
    <StackPanel Margin="0"
                Grid.Row="2">
      <TextBlock Height="Auto"
                 Width="Auto"
                 RenderTransformOrigin="0.5,0.5"
                 FontFamily="Georgia"
                 FontSize="18"
                 Text="from person in people"
                 TextWrapping="NoWrap"
                 HorizontalAlignment="Left" />
      <TextBlock Height="Auto"
                 RenderTransformOrigin="0.5,0.5"
                 FontFamily="Georgia"
                 FontSize="18"
                 Text="where person.LastName.StartsWith
                     (&quot;M&quot;)"
                 TextWrapping="NoWrap"
                 Width="Auto"
                 HorizontalAlignment="Left" />
      <TextBlock RenderTransformOrigin="0.5,0.5"
                 FontFamily="Georgia"
                 FontSize="18"
                 Text="select person"
                 TextWrapping="NoWrap"
                 Width="334"
                 Height="20"
                 HorizontalAlignment="Left" />
    </StackPanel>
    <StackPanel Margin="0,0,0,0"
                Grid.Row="3">
      <TextBlock x:Name="QOEM"
                 FontFamily="Georgia"
                 FontSize="18"
                 Text="people.Where ( 
    person =&gt; person.LastName.StartsWith ( &quot;M&quot; );"
                 TextWrapping="Wrap"/>
    </StackPanel>
    <Button x:Name="Go"
            Background="#FF00FF00"
            FontFamily="Georgia"
            FontSize="24"
            Foreground="#FF0000FF"
            Margin="5"
            Grid.Column="1"
            Grid.Row="5"
            Content="Go!" />
  </Grid>
</UserControl>

Page.xaml.cs

using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace ExtensionPart2
{
   public partial class Page : UserControl
   {
      public Page()
      {
         InitializeComponent();
         Go.Click += new RoutedEventHandler( Go_Click );
      }

      void Go_Click( object sender, RoutedEventArgs e )
      {
         List<Person> people = Person.GetPeople();

         // bind entire collection
         PresidentsListBox.ItemsSource = people;

         // traditional linq statement
         LinqResults.ItemsSource = from person in people
                            where person.LastName.StartsWith( "M" )
                            select person;

         // extended method
         QOEM_Results.ItemsSource = people.Where(
person => person.LastName.StartsWith( "M" ) ); } // end go button event handler } // end class } // end namespace
Previous: Extension Methods Part 1 
 

This work is licensed under a Creative Commons Attribution By license.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in z Silverlight Archives. Bookmark the permalink.