Partager via


LINQFarm: Understanding IEnumerable, Part I

The IEnumerable<T> interface is a key part of LINQ to Objects and binds many of its different features together into a whole. This series of posts explains IEnumerable<T> and the role it plays in LINQ to Objects. If you hear people talking about IEnumerable<T>, and sometimes wished you better understood its significance, then you should find this text helpful.

Collections and IEnumerable<T>

Though LINQ to Objects can be used to query several C# types, it cannot be used against all your in-process data sources. Those that can be queried all support the IEnumerable<T> interface. These include the generic collections found in the System.Collections.Generic namespace . The commonly used types found in this namespace include List<T> , Stack<T> , LinkedList<T> , Queue<T> , Dictionary<TKey, Value> and Hashset<T> .

All of the collections in the System.Collections.Generic namespace support the IEnumerable<T> interface. Here, for instance, is the declaration for List<T>:

 public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable

You will find IEnumerable<T> listed for all the other generic collections. It is no coincidence that these collections support IEnumerable<T>. Their implementation of this interface makes it possible to query them using LINQ to Objects.

LINQ to Objects and IEnumerable<T>

Consider the following simple LINQ query:

 List<int> list = new List<int> { 1, 3, 2 };
 // The LINQ Query expression
var query = from num in list
            where num < 3
            select num;
 foreach (var item in query)
{
    Console.WriteLine(item);
}

The type IEnumerable<T> plays two key roles in this code.

  • The query expression has a data source called list which implements IEnumerable<T>. 
  • The query expression returns an instance of IEnumberable<T>.

Every LINQ to Objects query expression, including the one shown above, will begin with a line of this type:

from x in y

In each case, the data source represented by the variable y must support the IEnumerable<T> interface. As you have already seen, the list of integers shown in this example supports that interface.

The same query shown here could also be written as follows:

 IEnumerable<int> query = from num in list
                         where num < 3
                         select num;

This code makes explicit the type of the variable returned by this query. As you can see, it is of type IEnumerable<int>. In practice, you will find that most LINQ to Objects queries return IEnumerable<T>, for some type T. The only exceptions are those that call a LINQ query operator that return a simple type, such as Count():

 int number = (from num in list
              where num < 3
              select num).Count();

In this case the query returns an integer specifying the number of items in the list created by this query. LINQ queries that return a simple type like this are an exception to the rule that LINQ to Objects queries operate on class that implement IEnumerable<T> and return an instance that supports IEnumerable<T>.

Composable

The fact that LINQ to Objects queries both take and return IEnumerable<T> enables a key feature of LINQ called composability. Because LINQ queries are composable you can usually pass the result of one LINQ query to another LINQ query. This allows you to compose a series of queries that work together to achieve a single end:

 List<int> list = new List<int> { 1, 3, 2 };

var query1 = from num in list
             where num < 3
             select num;

var query2 = from num in query1
             where num > 1
             select num;

var query3 = from num1 in query1
             from num2 in query2
             select num1 + num2;

Here the results of the first query are used as the data source for the second query, and the results of the first two queries are both used as data sources for the third query. If you print out the results of query3 with a foreach loop you get the numbers 3 and 4. Though it is not important to the current subject matter, you might have fun playing with the code to understand why these values are returned.

Summary

By now it should be clear to you that IEnumerable<T> plays a central role in LINQ to Objects. A typical LINQ to Objects query expression not only takes a class that implements IEnumerable<T> as its data source, but it also returns an instance of this same type. The fact that it takes and returns the same type enables a feature called composability.

The next logical question would be to ask why this type plays such a key role in LINQ to Objects. One simple answer would be that the creators of LINQ decided that it should be so, and hence it is so. But one can still ask why they picked this particular type. What is it about IEnumerable<T> that makes it a useful data source and return type for LINQ to Objects queries? The answer to that question will be found in the second part of this series of articles.

kick it on DotNetKicks.com

Comments

  • Anonymous
    April 28, 2008
    You've been kicked (a good thing) - Trackback from DotNetKicks.com

  • Anonymous
    April 28, 2008
    It's true that IEnumerable<T> is the central interface in linq, but actually, you can use link on any class or interface as long as you provides Select, SelectMany and other methods (as instance methods or extension methods). You can then use linq syntax as a kind of monade to work on augmented types... there is some posts about it on functional programming blogs...

  • Anonymous
    April 29, 2008
    Ref:http://blogs.msdn.com/charlie/archive/2008/04/28/linqfarm-understanding-ienumerable-t-sets-and-s...

  • Anonymous
    April 29, 2008
    Skup, Yes, you are right about the importance of extension methods and query operators. I will talk about them later in this series of posts. IEnumerable<T> is not essential to LINQ in general, as shown by LINQ to SQL, which uses IQueryable<T> instead, but it is an essential part of LINQ to Objects, which is my focus here.

  • Charlie
  • Anonymous
    April 29, 2008
    Don't forget that IEnumerable<T> is also supported by T[], which extends LINQ to Objects to a lot of data returned from APIs that hand you arrays (one common example being string.Split()). Also, String implements IEnumerable<char> which is occasionally useful. In addition, LINQ to Objects also extends the non-generic IEnumerable with Cast<T>() and OfType<T>() methods that convert nongeneric collections to IEnumerable<T>, exposing them to all the power of the LINQ query operators.

  • Anonymous
    May 06, 2008
    The comment has been removed

  • Anonymous
    June 09, 2008
    The comment has been removed

  • Anonymous
    January 14, 2009
    Thanks for the info, I have one question though. Is there anyway to return anything other than IEnumerable? e.g. If I have a IList<object> that I want to filter using Linq to Objects. Anything that I use gives me a IEnumerable collection wich I then have to manually traverse and add each item to a new IList<Object>. Just does not seem very elegant.

  • Anonymous
    January 14, 2009
    found my answer: System.Collections.Generic.IList<Object> objects = (System.Collections.Generic.IList<Object>)q.ToList();

  • Anonymous
    July 07, 2010
    "What is it about IEnumerable<T> that makes it a useful data source and return type for LINQ to Objects queries? The answer to that question will be found in the second part of this series of articles." Perhaps I've overlooked it and the search was no helpful, is Part II available?

  • Anonymous
    August 31, 2010
    The comment has been removed

  • Anonymous
    February 25, 2013
    very simple,Clear and usefull explantion...