Home

The .NET Framework Support for the LINQ

   

The IEnumerable Generic Interface

 

Introduction

We know how to create a LINQ statement by declaring a variable using the var keyword. The var keyword allows you to let the compiler figure out what type of value the statement would produce. To assist you with creating LINQ statements, the .NET Framework provides many interfaces and classes. The interfaces and the classes that implement them are created in the System.Linq namespace defined in the System.Core.dll assembly.

The Enumerable Class

In the .NET Framework, the language integrated query, LINQ, is implemented and supported by a class named Enumerable created in the System.Linq namespace defined in the System.Core.dll assembly. In fact, the LINQ is actually a "visual" or "real" way to use the Enumerable class. This means that, instead of creating a LINQ query, you can use the Enumerable class to query your collection. The problem is that, with Enumerable, you may have to write complex code that involves delegates and/or lambda expressions. As an alternative, the LINQ provides an easier way to create a query with easily recognizable words. Both techniques produce the same result(s). The important thing to know is that, while the LINQ's role is to make your life easier, if you create a LINQ statement, when it executes, the compiler would actually translate your LINQ statement into an implementation of an Enumerable expression. This means that when you use a LINQ statement, you are actually using an Enumerable object. In reality, Enumerable is a static class:

public static class Enumerable

This means that we will never declare a variable of type Enumerable. We will simply and directly use it. Still, the Enumerable class has all the methods you need to query a collection.

The IEnumerable Collection Interface

As you may know already, a query is primarily a a collection of records. The primary classes that support collections in the .NET Framework are defined in the System.Collection namespace. To allow the various collections to support iteration, the System.Collection namespace provides an interface named IEnumerable whose only method is named GetEnumerator:

IEnumerator GetEnumerator()

The IQueryable Interface

The LINQ came alive with the .NET Framework 3.5. To support the ability to query a collection class, a new interface named IQueryable was added to the System.Linq namespace:

public interface IQueryable : IEnumerable

When the System.Collection.Collection.IEnumerable interface was created in the .NET Framework 3.0, the LINQ was not yet implemented. Later on in the .NET Framework 3.5, to allow the System.Collection.Collection.IEnumerable interface to support LINQ querying, a new method named AsQueryable was added to it as an extension method. Its syntax is:

public static IQueryable AsQueryable(this IEnumerable source);

This method allows an interface and its class implementers to support LINQ.

The IEnumerable Generic Interface

As we have seen so far, a LINQ query consists of selecting one or more records from a list or collection. The list is treated as a collection of records. The lists and generic collections are primarily supported in the .NET Framework from a namespace named System.Collections.Generic. In that namespace, the main interface used to "scan" (or iterate through) a collection is a generic interface named IEnumerable. This interface is derived from its non-generic counterpart:

public interface IEnumerable<out T> : IEnumerable

To make it possible to iterate through a query, all the methods of the Enumerable class were added as extension methods to the IEnumerable<> generic interface. As a result, to decalre a variable for a query, you have various options. In many cases, you can declare the collection variable using the data type of the type of collection the query would produce. In previous sections, we declared the variables as arrays. Here is an example:

private void ExerciseLoad(object sender, EventArgs e)
{
    double[] numbers = new double[] { 12.44, 525.38, 6.28, 2448.32, 632.04 };

    var number = from n in numbers select n;

    foreach (var member in number)
        lbxNumbers.Items.Add(member.ToString());
}

 Because a query starts from a collection, you can declare the collection variable using IEnumerable<>. Inside < and >, pass the type of values in the collection. Here is an example:

private void ExerciseLoad(object sender, EventArgs e)
{
 IEnumerable<double> numbers = new double[] { 12.44, 525.38, 6.28, 2448.32, 632.04 };

    var number = from n in numbers select n;

    foreach (var member in number)
        lbxNumbers.Items.Add(member.ToString());
}

Because a query returns a collection, you can declare its variable as IEnumerable<>. Here is an example:

private void ExerciseLoad(object sender, EventArgs e)
{
 IEnumerable<double> numbers = new double[] { 12.44, 525.38, 6.28, 2448.32, 632.04 };

    IEnumerable<double> number = from n in numbers select n;

    foreach (var member in number)
        lbxNumbers.Items.Add(member.ToString());
}

In most cases, you should declare your query variable as var.

Introduction to List

All of the collections we have used so far were created as arrays. As a reminder, the System.Array class starts as follows:

[SerializableAttribute]
[ComVisibleAttribute(true)]
public abstract class Array : ICloneable, 
                              IList,
                              ICollection,
                              IEnumerable,
                              IStructuralComparable,
                              IStructuralEquatable

Notice that the System.Array class implements the IEnumerable interface. As you may know already, probably the most fundamental class that supports collections in the .NET Framework (since the 2.0 release) is the generic List<> class defined in the System.Collections.Generic interface. As a reminder, the System.Collections.Generic.List<> class starts as follows:

[SerializableAttribute]
public class List<T> : IList<T>,
                       ICollection<T>, 
		       IList,
		       ICollection,
		       IReadOnlyList<T>,
		       IReadOnlyCollection<T>,
		       IEnumerable<T>,
		       IEnumerable

As you can see, the System.Collections.Generic.List<> class implements both the generic IEnumerable<> and the non-generic IEnumerable interfaces. This means that you can use the List<> class to declare a collection variable used in LINQ. Here is an example:

private void Exercise_Load(object sender, EventArgs e)
{
    var numbers = new List<double>();

    numbers.Add(12.44);
    numbers.Add(525.38);
    numbers.Add(6.28);
    numbers.Add(2448.32);
    numbers.Add(632.04);
}

After creating the collection, you can use the same LINQ formula we saw for an array. Here is an example:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Fundamentals
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            var numbers = new List<double>();

            numbers.Add(12.44);
            numbers.Add(525.38);
            numbers.Add(6.28);
            numbers.Add(2448.32);
            numbers.Add(632.04);

            var number = from n
                         in numbers
                         select n;

            foreach (var member in number)
                lbxNumbers.Items.Add(member.ToString());
        }
    }
}

The LINQ as an IEnumerable Object

As mentioned above, a LINQ query is in fact an implementation of the IEnumerable<> interface. As a consequence, every operation we have performed so far is implemented in the interface as a method.

Instead of processing a query locally, you can hand the job to an external method that would return the query. To create a method that returns a query, specify its return type as IEnumerable. If the method will receive the value it must process, you can pass it as argument. In the body of the method, create a (the) LINQ statement(s) as you see fit. Before the method closes, make sure it returns a value that either is of type IEnumerable or implements that interface. Here is an example:

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class Exercise : Form
{
    private ListBox lbxNumbers;

    public Exercise()
    {
        InitializeComponent();
    }

    void InitializeComponent()
    {
        lbxNumbers = new ListBox();
        lbxNumbers.Location = new System.Drawing.Point(12, 12);
        lbxNumbers.Width = 100;

        Text = "Numbers";
        MinimizeBox = false;
        MaximizeBox = false;
        Controls.Add(this.lbxNumbers);
        Load += new EventHandler(ExerciseLoad);
        Size = new System.Drawing.Size(130, 145);
        StartPosition = FormStartPosition.CenterScreen;
    }

    IEnumerable GetQuery(double[] list)
    {
        return from n in list select n;
    }

    private void ExerciseLoad(object sender, EventArgs e)
    {
        var numbers = new double[] { 12.44, 525.38, 6.28, 2448.32, 632.04 };

        var number = GetQuery(numbers);

        foreach (var member in number)
            lbxNumbers.Items.Add(member.ToString());
    }

    [STAThread]
    public static int Main()
    {
        System.Windows.Forms.Application.Run(new Exercise());
        return 0;
    }
}

In this example, we used a method that returned a list of simple values. In the same way, you can create a method that returns a list of objects. You can also create a method that returns a single value from a query.

Characteristics of an Enumerable

   

Selecting Records

The primary operation performed in a query consists of selecting records. As you should know already, the LINQ supports that operation through the select keyword. Here is an example:

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class MusicStore : Form
{
    private ColumnHeader colItemCode;
    private ColumnHeader colCategory;
    private ColumnHeader colItemName;
    private ColumnHeader colUnitPrice;

    ListView lvwStoreItems;

    public MusicStore()
    {
        InitializeComponent();
    }

    void InitializeComponent()
    {
        lvwStoreItems = new ListView();
        lvwStoreItems.Anchor = AnchorStyles.Left  | AnchorStyles.Top |
                               AnchorStyles.Right | AnchorStyles.Bottom;
        lvwStoreItems.FullRowSelect = true;
        lvwStoreItems.GridLines = true;
        lvwStoreItems.Location = new Point(12, 12);
        lvwStoreItems.Size = new System.Drawing.Size(530, 137);
        lvwStoreItems.View = View.Details;

        colItemCode = new ColumnHeader();
        colItemCode.Text = "Item #";
        colItemCode.Width = 50;
        lvwStoreItems.Columns.Add(colItemCode);

        colCategory = new ColumnHeader();
        colCategory.Text = "Category";
        colCategory.Width = 105;
        lvwStoreItems.Columns.Add(colCategory);

        colItemName = new ColumnHeader();
        colItemName.Text = "Item Name/Description";
        colItemName.Width = 310;
        lvwStoreItems.Columns.Add(colItemName);

        colUnitPrice = new ColumnHeader();
        colUnitPrice.Text = "Unit Price";
        colUnitPrice.TextAlign = HorizontalAlignment.Right;
        lvwStoreItems.Columns.Add(colUnitPrice);

        Text = "Music Store";
        MaximizeBox = false;
        Size = new System.Drawing.Size(562, 188);
        Controls.Add(lvwStoreItems);
        Load += new EventHandler(VideoCollectionLoad);
    }

    private void VideoCollectionLoad(object sender, EventArgs e)
    {
        var storeItems = new StoreItem[]
        {
            new StoreItem(925173, "Acoustic Guitars", "Yamaha F335 Acoustic Guitar", 129.85),
            new StoreItem(274930, "Drums", "Ludwig Accent Combo with Zildjian ZBT Cymbal Set", 595.85),
            new StoreItem(974951, "Microphone Cables", "Live Wire Advantage Standard EXM Series Microphone Cable", 14.95),
            new StoreItem(208148, "Electric Bass", "ESP LTD EC-256FM Electric Guitar", 395.95), 
            new StoreItem(802840, "Acoustic Guitars", "Taylor Big Baby Taylor Acoustic Guitar", 449.95),
            new StoreItem(524037, "Electric Bass", "Fender Deluxe P Bass Special 4-String Bass", 699.95),
            new StoreItem(274703, "Synthesizers", "Access Virus TI v2 Keyboard", 2992.25),
            new StoreItem(408408, "Speakers", "Behringer EUROLIVE B210D Active PA Speaker System", 195.95)
        };

        IEnumerable<StoreItem> sale = from items
	        	              in storeItems
        	        	      select items;

        foreach (var item in sale)
        {
            ListViewItem lviCollection = new ListViewItem(item.ItemCode.ToString());

            lviCollection.SubItems.Add(item.Category);
            lviCollection.SubItems.Add(item.ItemName);
            lviCollection.SubItems.Add(item.UnitPrice.ToString());
            lvwStoreItems.Items.Add(lviCollection);
        }
    }

    [STAThread]
    public static int Main()
    {
        System.Windows.Forms.Application.Run(new MusicStore());
        return 0;
    }
}

public class StoreItem
{
    public int    ItemCode  { get; set; }
    public string Category  { get; set; }
    public string ItemName  { get; set; }
    public double UnitPrice { get; set; }

    public StoreItem(int code = 0,
                     string type = "",
                     string name = "",
                     double price = 0.00)
    {
        ItemCode  = code;
        Category  = type;
        ItemName  = name;
        UnitPrice = price;
    }
}

To support the select operation, the IEnumerable<> generic interface is equipped with the Select() method that is overloaded with two versions whose syntaxes are:

public static IEnumerable<TResult> Select<TSource, TResult>(
	this IEnumerable<TSource> source,
	Func<TSource, TResult> selector);
public static IEnumerable<TResult> Select<TSource, TResult>(
	this IEnumerable<TSource> source,
	Func<TSource, int, TResult> selector);

This method expects a variable that would hold the result of the selection. You can pass it a lambda expression. This can be done as follows:

IEnumerable<StoreItem> sale = storeItems.Select<StoreItem, StoreItem>(items => items);

You don't have to specify the TSource and the TResult classes. After doing this, the IEnumerable variable can be used in a foreach loop. Here is an example:

IEnumerable<StoreItem> sale = storeItems.Select(items => items);

foreach (var item in sale)
{
    ListViewItem lviCollection = new ListViewItem(item.ItemCode.ToString());

    lviCollection.SubItems.Add(item.Category);
    lviCollection.SubItems.Add(item.ItemName);
    lviCollection.SubItems.Add(item.UnitPrice.ToString());
    lvwStoreItems.Items.Add(lviCollection);
}

This would produce:

Selecting Records

Where is the Enumerable?

To support conditions of a query, the Enumerable class is equipped with a method named Where that is overloaded with two versions. The syntax of one is:

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source,
						  Func<TSource, bool> predicate);

Here is an example of calling it:

 IEnumerable<StoreItem> sale = storeItems.Where(item => item.Category == "Electric Bass");

This would produce:

Where is the Enumerable?

Although we directly applied the Where() method to the collection variable (storeItems), you can create a query and apply to the method. Here is an example:

IEnumerable<StoreItem> sale = (from storeItem in storeItems select storeItem).Where(item => item.Category == "Electric Bass");

This is practically the same way the where keyword was used in previous sections.

Sorting the Result of a Query

If you have a simple list of values of a primitive type and the LINQ variable is declared using the var keyword, we saw that you could sort it using the orderby operator. Here is an example:

IEnumerable<StoreItem> sale = from storeItem
                              in storeItems
                              orderby storeItem.UnitPrice
                              select storeItem;
 
foreach (var item in sale)
{
    ListViewItem lviCollection = new ListViewItem(item.ItemCode.ToString());

    lviCollection.SubItems.Add(item.Category);
    lviCollection.SubItems.Add(item.ItemName);
    lviCollection.SubItems.Add(item.UnitPrice.ToString());
    lvwStoreItems.Items.Add(lviCollection);
}

To support the ability to sort the values, the Enumerable class provides its own OrderBy() method that is overloaded with two versions. Their syntaxes are:

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
	this IEnumerable<TSource> source,
	Func<TSource, TKey> keySelector);
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
	this IEnumerable<TSource> source,
	Func<TSource, TKey> keySelector,
	IComparer<TKey> comparer);

Here is an example:

IEnumerable<StoreItem> sale = storeItems.OrderBy(item => item.UnitPrice);
 
foreach (var item in sale)
{
    ListViewItem lviCollection = new ListViewItem(item.ItemCode.ToString());

    lviCollection.SubItems.Add(item.Category);
    lviCollection.SubItems.Add(item.ItemName);
    lviCollection.SubItems.Add(item.UnitPrice.ToString());
    lvwStoreItems.Items.Add(lviCollection);
}

This would produce:

Sorting the Result of a Query

To support sorting in reverse order the Enumerable class is equipped with the OrderByDescending() method that is overloaded with two versions.

Imagine you want to give the user the ability to sort the list but you want the list to be accessible from sections of the code. One way you can solve this problem is to declare a LINQ variable outside of the code sections but without initializing the variable, as long as you make sure the variable would be initialized before it is used. The problem with the var keyword is that you must initialize its variable when you declare it. The advantage with an IEnumerable variable is that you do not have to initialize it when declaring it. You can declare it, then specify its value in another section of the code.

 
 
 

LINQ and Numbers

 

A Range of Numbers

We know how to create an array of (random) natural numbers and store it in a variable to use a query. Here is an example:

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class Exercise : Form
{
    private ListBox lbxNumbers;

    public Exercise()
    {
        InitializeComponent();
    }

    void InitializeComponent()
    {
        lbxNumbers = new ListBox();
        lbxNumbers.Size = new Size(100, 140);
        lbxNumbers.Location = new System.Drawing.Point(12, 12);

        Text = "Numbers";
        MinimizeBox = false;
        MaximizeBox = false;
        Controls.Add(this.lbxNumbers);
        Load += new EventHandler(ExerciseLoad);
        Size = new System.Drawing.Size(130, 185);
        StartPosition = FormStartPosition.CenterScreen;
    }

    private void ExerciseLoad(object sender, EventArgs e)
    {
        var numbers = new int[] { 12, 44, 525, 38, 6, 28, 2448, 32, 632, 04 };
        var number = from n in numbers select n;

        foreach (var member in number)
            lbxNumbers.Items.Add(member.ToString());
    }

    [STAThread]
    public static int Main()
    {
        System.Windows.Forms.Application.Run(new Exercise());
        return 0;
    }
}

This would produce:

Numbers

In some cases, you may want to work on a consecutive list of numbers such as 1, 2, 3, 4, 5, 6, 7, and 8. Instead of declaring a formal variable, the Enumerable class provides a method named Range that allows you to specify the first number of a range and a count of consecutive numbers to add to create a range. The syntax of the Enumerable.Range() method is:

public static IEnumerable<int> Range(int start, int count);

The first argument passed to this method is the beginning of the range. The second argument specifies how many numbers to add consecutively from the first. To use this method, you can declare a variable of type IEnumerable and assign a call to Enumerable.Range() that receives both arguments. Here is an example:

private void ExerciseLoad(object sender, EventArgs e)
{
    IEnumerable<int> range = Enumerable.Range(22, 8);
    var number = from n in range select n;

    foreach (var member in number)
        lbxNumbers.Items.Add(member.ToString());
}

You can also declare the variable as type var. You would receive the same result:

Range

If you want to restrict the result, you can add a where condition to it. Here is an example:

private void ExerciseLoad(object sender, EventArgs e)
{
    var range = Enumerable.Range(96, 10);
    var number = from n
                 in range
                 where n % 2 == 0
                 select n;

    foreach (var member in number)
        lbxNumbers.Items.Add(member.ToString());
}

Counting the Number of Records

When you create a LINQ statement, it produces a list. Although the list is of type IEnumerable, since this is only an interface, the result relies on an actual class to provide its characteristics. The class that gives you information about a result is Enumerable. The Enumerable.Count() method is used to know the number of items in the resulting query. That method is overloaded with two versions whose syntaxes are:

public static int Count<TSource>(
	this IEnumerable<TSource> source);
public static int Count<TSource>(
	this IEnumerable<TSource> source,
	Func<TSource, bool> predicate);

 Here is an example:

IEnumerable<StoreItem> sale = storeItems.Select(item => item);
 
foreach (var item in sale)
{
    ListViewItem lviCollection = new ListViewItem(item.ItemCode.ToString());

    lviCollection.SubItems.Add(item.Category);
    lviCollection.SubItems.Add(item.ItemName);
    lviCollection.SubItems.Add(item.UnitPrice.ToString());
    lvwStoreItems.Items.Add(lviCollection);
}

Text = "Music Store - Total Inventory: " + sale.Count().ToString() + " store items";

This would produce:

Counting the items in the query

Remember that you can still use the var keyword to declare the variable that would hold the resulting list. The same var keyword can be used for the result of a method call. Here are examples:

IEnumerable empls = from staffMembers
                         in employees
                         orderby staffMembers.LastName
                         select staffMembers;
var total = empls.Count();

Since we have determined that a LINQ statement produces an Enumerable list, if you don't need the list itself, you can declare a variable that is the type returned by a method, put the statement in parentheses, and then access the method outside the closing parenthesis using the period operator. Here is an example:

var total = (from staffMembers
             in employees
             orderby staffMembers.LastName
             select staffMembers).Count();

Remember that the IEnumerable.Count() method returns the number of items in the result of the LINQ statement, not the number of items in the original list. The following examples illustrate it:

private void Exercise_Load(object sender, EventArgs e)
{
    var students = new Student[]
    {
        . . . No Change
    };

    IEnumerable<Student> pupils = from studs
                                  in students
                                  select studs;
    int boys = (from males
                in pupils
                where males.Gender == Genders.Male
                select males).Count();

    int girls = (from females
                 in pupils
                 where ((females.Gender != Genders.Male) && 
			(females.Gender != Genders.Unknown))
                 select females).Count();

    ListViewItem lviStudent = null;
    ListViewGroup grpBoys = new ListViewGroup("Boys");
    ListViewGroup grpGirls = new ListViewGroup("Girls");
    ListViewGroup grpOthers = new ListViewGroup("Others");

    foreach (var std in pupils)
    {
        if (std.Gender == Genders.Male)
        {
            lviStudent = new ListViewItem(std.StudentNumber.ToString(), grpBoys);
        }
        else if (std.Gender == Genders.Female)
        {
            lviStudent = new ListViewItem(std.StudentNumber.ToString(), grpGirls);
        }
        else
        {
            lviStudent = new ListViewItem(std.StudentNumber.ToString(), grpOthers);
        }

        lviStudent.SubItems.Add(std.FirstName);
        lviStudent.SubItems.Add(std.LastName);
        lvwStudents.Items.Add(lviStudent);

        lvwStudents.Groups.Add(grpBoys);
        lvwStudents.Groups.Add(grpGirls);
        lvwStudents.Groups.Add(grpOthers);
    }

    txtBoys.Text = boys.ToString();
    txtGirls.Text = girls.ToString();
}

This would produce:

Students

These different techniques of calling and using the Count() method will be applied to most other methods of the Enumerable class.

An Average of Numbers

If the values you are querying are numbers, you may want to find their average. To assist you, the Enumerable class provides a method named Average that is overloaded with a version for each numeric data type. The syntax for the double type is:

public static double Average(this IEnumerable source);

Here is an example of calling this method:

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class Exercise : Form
{
    private ListBox lbxNumbers;
    private Label lblAverage;
    private TextBox txtAverage;

    public Exercise()
    {
        InitializeComponent();
    }

    void InitializeComponent()
    {
        lbxNumbers = new ListBox();
        lbxNumbers.Size = new Size(105, 80);
        lbxNumbers.Location = new System.Drawing.Point(12, 12);

        lblAverage = new Label();
        lblAverage.AutoSize = true;
        lblAverage.Text = "Average:";
        lblAverage.Location = new Point(12, 90);

        txtAverage = new TextBox();
        txtAverage.Width = 50;
        txtAverage.Location = new Point(68, 90);
        txtAverage.TextAlign = HorizontalAlignment.Right;

        Text = "Numbers";
        MinimizeBox = false;
        MaximizeBox = false;
        Controls.Add(lbxNumbers);
        Controls.Add(lblAverage);
        Controls.Add(txtAverage);

        Load += new EventHandler(ExerciseLoad);
        Size = new System.Drawing.Size(140, 150);
        StartPosition = FormStartPosition.CenterScreen;
    }

    private void ExerciseLoad(object sender, EventArgs e)
    {
        var numbers = new double [] { 12.44, 525.38, 6.28, 2448.32, 632.04 };
        
        var number = from n
                     in numbers
                     select n;

        foreach (var member in number)
            lbxNumbers.Items.Add(member.ToString());

        txtAverage.Text = number.Average().ToString();
    }

    [STAThread]
    public static int Main()
    {
        System.Windows.Forms.Application.Run(new Exercise());
        return 0;
    }
}

This would produce:

Average

If it is only the average that you want, you can include the LINQ statement in parentheses and call the Average method outside. Here is an example:

var average = (from n
               in numbers
               select n).Average();

txtAverage.Text = average.ToString();

Of course, you can add a where condition if you want to restrict the result. The LINQ provides many more methods for numeric values.

LINQ and Arrays

 

Converting a LINQ Result to an Array or List

We have seen that a LINQ expression produces a list of values and, so far, the resulting list was stored in a variable created in a from clause. If you want, you can store the result of the LINQ expression in a specific (or external) variable. To support this, the Enumerable class provides the ToArray() and the ToList() methods. The syntax of the ToArray() method is:

public static TSource[] ToArray<TSource>(
	this IEnumerable<TSource> source;)

To store the results of an IEnumerable list in an array, declare the array variable and assign the resulting list to it. Here is an example:

IEnumerable<Student> pupils = from studs
                              in students
                              // orderby studs.LastName
                              //where studs.Gender == Genders.Female 
                              select studs;
Student[] attendees = pupils.ToArray();

foreach (var std in attendees)
{
    ListViewItem lviStudent = new ListViewItem(std.StudentNumber.ToString());

    lviStudent.SubItems.Add(std.FirstName);
    lviStudent.SubItems.Add(std.LastName);
    lvwStudents.Items.Add(lviStudent);
}

Instead of first creating the select list, then declaring an array variable, you can include the LINQ statement in parentheses and call the ToArray() method outside the closing parenthesis. Here is an example:

Student[] pupils = (from studs
                    in students
                    // orderby studs.LastName
                    // where studs.Gender == Genders.Female 
                    select studs).ToArray();

foreach (var std in pupils)
{
    ListViewItem lviStudent = new ListViewItem(std.StudentNumber.ToString());

    lviStudent.SubItems.Add(std.FirstName);
    lviStudent.SubItems.Add(std.LastName);
    lvwStudents.Items.Add(lviStudent);
}

Instead of storing the result of a LINQ statement into an array, you can store it in a collection. To support this, the IEnumerable interface is equipped with the ToList() method. Its syntax is:

public static List<TSource> ToList<TSource>(
	this IEnumerable<TSource> source);

This method follows the same rules as its counterpart the ToArray() method except that you must declare a List<> generic variable for it. Here is an example:

IEnumerable<Student> pupils = from studs
                              in students
                              // orderby studs.LastName
                              // where studs.Gender == Genders.Female 
                              select studs;
List<Student> attendees = pupils.ToList();

You can then use the List<> variable as you see fit.

The String Class

 

Introduction

In the .NET Framework, the String class starts as follows:

public sealed class String : IComparable, 
                             ICloneable,
                             IConvertible,
                             IComparable<string>,
                             IEnumerable<char>,
                             IEnumerable,
                             IEquatable<string>

As you can see, the String class implements:

  • The IEnumerable interface. This means that it already ready for all possible operations of the LINQ
  • The IEnumerable<char> interface. This results in a string being a collection of characters. Consequently, all normal collection operations can be performed on a string and each character or symbol is treated as an item
  • the IComparable and the IComparable<string> interfaces. This gives the ability to compare two strings

Based on this, the String class holds a special position in LINQ. If you create a LINQ statement whose select expression produces a string value, the String class makes available its properties and methods so you can use them to refine your where statement.

Concatenating the Results of a Select Statement

We saw how you can use one value that is a member of an object of a class as produced by a select statement:

var lastNames = from n
                in empls 
                select n.LastName;

To add other values to the select statement, that is, to access more that one member of the class, you can concatenate them. The String class provides various options.

The String class is equipped with the Concat() method that is overloaded with 10 versions. You can use any of those versions to add strings. Here is an example:

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class Exercise : Form
{
    Button btnShow;
    ListBox lbxEmployees;

    Employee[] employees;

    public Exercise()
    {
        InitializeComponent();
    }

    void InitializeComponent()
    {
        employees = new Employee[10];

        btnShow = new Button();
        btnShow.Location = new System.Drawing.Point(12, 8);
        btnShow.Width = 75;
        btnShow.Text = "Show";
        btnShow.Click += new System.EventHandler(this.btnShowClick);

        lbxEmployees = new ListBox();
        lbxEmployees.Location = new Point(12, 40);
        lbxEmployees.Size = new System.Drawing.Size(180, 80);

        Size = new System.Drawing.Size(210, 150);
        Controls.Add(btnShow);
        Controls.Add(lbxEmployees);
        Text = "Employees";
    }

    private void btnShowClick(object sender, EventArgs e)
    {
        employees = new Employee[]
        {
            new Employee(971974, "Patricia", "Katts", 24.68M),
            new Employee(208411, "Raymond", "Kouma", 20.15M),
            new Employee(279374, "Hélène", "Mukoko", 15.55M),
            new Employee(707912, "Bertrand", "Yamaguchi", 24.68M),
            new Employee(971394, "Gertrude", "Monay", 20.55M)
        };

        var empls = from staffMembers
                    in employees
                    select string.Concat(staffMembers.EmployeeNumber.ToString(), ", ",
                                         staffMembers.FirstName, " ",
                                         staffMembers.LastName, ", ",
                                         staffMembers.HourlySalary.ToString());

        foreach (var empl in empls)
            lbxEmployees.Items.Add(empl);
    }

    [STAThread]
    public static int Main()
    {
        System.Windows.Forms.Application.Run(new Exercise());
        return 0;
    }
}

public class Employee
{
    public int EmployeeNumber;
    public string FirstName;
    public string LastName;
    public decimal HourlySalary;

    public Employee(int number = 0,
                       string firstName = "John",
                       string lastName = "Doe",
                       decimal salary = 0M)
    {
        EmployeeNumber = number;
        FirstName = firstName;
        LastName = lastName;
        HourlySalary = salary;
    }

    internal string GetFullName()
    {
        return LastName + ", " + FirstName;
    }
}

This would produce:

Employees

Alternatively, you can use the + operator that is overloaded in the String class. Here is an example:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace LINQFundamentals
{
    public class Employee
    {
        public int EmployeeNumber;
        public string FirstName;
        public string LastName;
        public double HourlySalary;
    }

    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void btnShow_Click(object sender, EventArgs e)
        {
            var empls = new Employee[5];
            
            . . . No Change

            var fullNames = from n
                            in empls 
                            select n.LastName + ", " + n.FirstName;

            foreach (var member in fullNames)
                lbxEmployees.Items.Add(member);
        }
    }
}

This would produce:

Employees

In the same way, you can use any of the regular C# operators to produce any result of your choice. Two other options consist of using either the Format() of the Join() methods (of the String class).

Checking for String Equality

Like all classes of the .NET Framework, the String class inherits the Equals() method of the Object class. This makes it possible to compare two strings for equality. Besides the Equals() method, the == operator is overloaded in the String class and has the same effect. Here is an example of using it:

private void Exercise_Load(object sender, EventArgs e)
{
    var names = new[]
    {
        "Hermine", "Patrick", "Hank", "Bertine",
        "Julius", "Thomas", "Jeannette", "Patrick",
        "Patrick", "Raul", "David", "Paulette"
    };

    var name = from n
               in names
               where n == "Patrick"
               select n;

    foreach (var member in name)
        lbxNames.Items.Add(member);
}

This would produce:

Names

If you are working on a class, you can apply the equality operator on one of its properties. Here is an example:

private void btnShowClick(object sender, EventArgs e)
{
    employees = new Employee[]
    {
            new Employee(971974, "Patricia", "Katts", 24.68M),
            new Employee(208411, "Raymond", "Kouma", 20.15M),
            new Employee(279374, "Hélène", "Mukoko", 15.55M),
            new Employee(707912, "Bertrand", "Yamaguchi", 24.68M),
            new Employee(294800, "Peter", "Mukoko", 18.85M),
            new Employee(971394, "Gertrude", "Monay", 20.55M)
    };

    var fullNames = from empls
                    in employees
                    where empls.LastName == "Mukoko"
                    select empls.LastName + ", " + empls.FirstName;

    foreach (var empl in fullNames)
        lbxEmployees.Items.Add(empl);
}

This would produce:

Employees

On the other hand, to find out if two strings are not equal, you can use the != operator.

Checking the Starting Sub-String

The String class is equipped with various types of methods to work on sub-strings. To get a list of strings that start with a certain letter or a sub-string, you can call the StartsWith() method. This is a Boolean method that is overloaded with three versions. One of the versions takes one argument that is the sub-string to find at the beginning of the string that calls it. Here is an example:

private void Exercise_Load(object sender, EventArgs e)
{
    var name = new[]
    {
        "Hermine", "Patrick", "Hank", "Bertine",
        "Julius", "Thomas", "Jeannette", "Patricia",
        "Henriette", "Raul", "David", "Paulette"
    };

    var names = from n
                in name
                where n.StartsWith("P")
                select n;

    foreach (var member in names)
         lbxStrings.Items.Add(member);
}

This would produce:

The String.StartsWith() method returns true if the variable that called it starts with the argument. You can negate the result to find the strings that don't start with the argument. To do this, apply the ! operation. Here is an example:

private void Exercise_Load(object sender, EventArgs e)
{
    var name = new[]
    {
        "Hermine", "Patrick", "Hank", "Bertine",
        "Julius", "Thomas", "Jeannette", "Patricia",
        "Henriette", "Raul", "David", "Paulette"
    };

    var names = from n
                in name
                where !n.StartsWith("P")
                select n;

    foreach (var member in names)
        lbxNames.Items.Add(member);
}

This would produce:

Names

To make the expression easy to read, you should include it in parentheses. Here is an example:

private void Exercise_Load(object sender, EventArgs e)
{
    var name = new[]
    {
        "Hermine", "Patrick", "Hank", "Bertine",
        "Julius", "Thomas", "Jeannette", "Patricia",
        "Henriette", "Raul", "David", "Paulette"
    };

    var names = from n
                in name
                where !(n.StartsWith("P"))
                select n;

    foreach (var member in names)
        lbxNames.Items.Add(member);
}

Checking the Ending Sub-String

To get a list of strings that end with a certain letter or sub-string, you would call the String.EndsWith() method. Here is an example:

private void Exercise_Load(object sender, EventArgs e)
{
    var name = new[]
    {
        "Hermine", "Patrick", "Hank", "Bertine",
        "Julius", "Thomas", "Jeannette", "Patricia",
        "Henriette", "Raul", "David", "Paulette"
    };

    var names = from n
                in name
                where n.EndsWith("ette")
                select n;

    foreach (var member in names)
         lbxStrings.Items.Add(member);
}

This would produce:

Names

To negate this operation, apply the ! operator to it.

Checking the Existence of a Sub-String

In the same way, to get a select list of strings that contain a certain symbol or a sub-string, you can call the String.Contains() method as follows:

private void Exercise_Load(object sender, EventArgs e)
{
    var name = new[]
    {
        "Hermine", "Patrick", "Hank", "Bertine",
        "Julius", "Thomas", "Jeannette", "Patricia",
        "Henriette", "Raul", "David", "Paulette"
    };

    var names = from n
                in name
                where n.Contains("au")
                select n;

    foreach (var member in names)
         lbxStrings.Items.Add(member);
}

This would produce:

Names

Because the String.Contains() method is Boolean, to negate its result, you can precede it with the ! operator.

 
 
   
 

Previous Copyright © 2008-2014, FunctionX Next