69

One of the extension methods on IEnumerable<T> is .AsEnumerable(). This method converts the enumerable object it was called on into an instance of IEnumerable<T>. However, since an object must implement IEnumerable<T> in order to apply to this extension method, converting to IEnumerable<T> is a simple matter of casting to IEnumerable<T>. My question is why does this method exist at all?

Example:

List<string> strings = new List<string>() { "test", "test2", "test3" };
IEnumerable<string> stringsEnum1 = strings.AsEnumerable();
IEnumerable<string> stringsEnum2 = (IEnumerable<string>)strings;

In the example above, stringsEnum1 and stringsEnum2 are equivalent. What's the point of the extension method?

As a corollary, why is there an .AsQueryable() method when casting to IQueryable<T> is equivalent?

Erik Forbes
  • 35,357
  • 27
  • 98
  • 122

7 Answers7

94

Readability is the main issue here. Consider that

Table.AsEnumerable().Where(somePredicate)

is far more readable than

((IEnumerable<TableObject>)Table).Where(somePredicate).

Or imagine wanting to execute part of the query on the SQL Server and the rest in memory:

Table.Where(somePredicate)
     .Select(someProjection)
     .AsEnumerable()
     .SomethingElse()

versus

((IEnumerable<SomeProjectionType>)Table.Where(somePredicate)
                                       .Select(someProjection))
                                       .SomethingElse()

Now, as for why such a method is useful at all think of the example of a Table in a LINQ to SQL DataContext. As Table is an IQueryable it implements IEnumerable. When you invoke a Where method on such a Table and enumerate through the results, code is executed that eventually causes a SQL statement to be executed on a SQL Server. What AsEnumerable does is says, no, I don't want to use the LINQ to SQL provider to execute the Where, I want to use the LINQ to Objects implementation of Where.

Thus enumerating over

Table.Where(somePredicate)

causes a query to be executed on a SQL Server whereas enumerating over

Table.AsEnumerable().Where(somePredicate)

brings the table represented by Table into memory and executes the Where functionality in memory (and not on the SQL Server!)

This is the point of AsEnumerable: to allow you to hide a specific implementation of IEnumerable methods and instead use the standard implementation.

jason
  • 236,483
  • 35
  • 423
  • 525
  • 2
    This is true - but wouldn't casting to `IEnumerable` provide the same result? – Erik Forbes Jan 06 '10 at 15:18
  • 6
    I find `Table.AsEnumerable().Where(somePredicate)` to be far more readable than `((IEnumerable)Table).Where(somePredicate)`. – jason Jan 06 '10 at 15:25
  • 33
    I've got it - they're technically equivalent, but AsEnumerable is more capable due to anonymous types: you can't cast to an IEnumerable of anonymous types, but AsEnumerable can. – Erik Forbes Jan 06 '10 at 15:48
  • 3
    That's a really great point. I do consider that a side benefit though; I think the main point is readability. – jason Jan 06 '10 at 15:52
  • Perhaps, but using AsEnumerable is the *only* way to do something like what I've got in my example. If you couldn't use AsEnumerable, you'd have to create a new type every time you wanted to do this, and not use anonymous types at all. – Erik Forbes Jan 06 '10 at 15:54
  • 1
    @Erik: You could, of course, create your own extension method to do the cast to `IEnumerable`, but in that case you'd effectively be re-implementing `AsEnumerable`. – LukeH Jan 06 '10 at 16:00
  • Similarly i would like to enlighten one more example: - Convert.ToString() and .ToString() both exist and can be used interchangeably and sole the same purposes, but .ToString() is far more readable. – King Jul 26 '17 at 10:06
  • 1
    They are _not_ equivalent. The cast simply forces the current result set to IEnumerable, additional operations will be added to the existing expression tree. AsEnumerable() starts a new expression tree for local execution. Using AsEnumerable allows you to have remote and local methods in the same query, casting does not. – Richard Petheram Feb 28 '18 at 11:22
  • @RichardPetheram That is incorrect. The documentation for [AsEnumerable](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.asenumerable?view=netframework-4.7.2#remarks) states that it only changes the type of the object, which of course changes the set of extension methods which are available to run against the object. – jpaugh Apr 16 '19 at 22:45
  • @jpaugh see my answer [elsewhere in this topic](https://stackoverflow.com/a/6109816/657572) for relevant details. However, since I now use this technique on a routine basis I'm confident that my comment is correct. – Richard Petheram Apr 17 '19 at 11:38
  • @RichardPetheram I appreciate your reply. The results you see in your answer are exactly what I would expect from casting. In fact, [I tried it](https://gist.github.com/jpaugh/f080c9c83add05ec28394be3d24ff8f6), and it works the same way as `AsEnumerable`. – jpaugh Apr 17 '19 at 13:39
  • Casting to IEnumerable has no effect as IQueryable already implements IEnumerable. Also, casting to IEnumerable will work just fine if the original query is running locally. It gets tricky when the query is running against a remote source, so MySQLSource.MyTable.Where(SQlPredicate).Where(LocalPredicate) will _not_ work, and MySQLSource.MyTable.Where(SQlPredicate).IEnumerable().Where(LocalPredicate) will _not_ work, but MySQLSource.MyTable.Where(SQlPredicate).AsEnumerable().Where(LocalPredicate) _will_ work. – Richard Petheram Apr 19 '19 at 13:09
16

I've thought of a reason apart from readability, though related to query implementation: using Linq to Objects on anonymous types returned via another Linq provider. You can't cast to an anonymous type (or a collection of anonymous types), but you can use .AsEnumerable() to perform the cast for you.

Example:

// Get an IQueryable of anonymous types.
var query = from p in db.PeopleTable /* Assume Linq to SQL */
            select new { Name = p.Name, Age = p.Age };

// Execute the query and pull the results into an IEnumerable of anonymous types
var @enum = query.AsEnumerable();

// Use Linq to Objects methods to further refine.
var refined = from p in @enum
              select new
              {
                  Name = GetPrettyName(p.Name),
                  DOB = CalculateDOB(p.Age, DateTime.Now)
              };

Clearly the reason here is that we want to use something like Linq to SQL to pull down some records into an anonymous type, then perform some custom logic (that wouldn't be possible via Linq to SQL) using Linq to Objects on the client-side.

Casting to IEnumerable<_anon> isn't possible, so .AsEnumerable() is the only way to go.

Thanks everyone who answered to help me piece this together. =)

Erik Forbes
  • 35,357
  • 27
  • 98
  • 122
  • Your second comment in your C# example is incorrect. Calling AsEnumerable() doesn't execute the query. See the accepted answer here: https://stackoverflow.com/questions/3389855/am-i-misunderstanding-linq-to-sql-asenumerable – David Klempfner Oct 07 '19 at 06:18
4

As I'm reading the book C# 6.0 in a Nutshell. Below is an example of AsEnumerable in the book.


The purpose is to cast an IQueryable<T> sequence to IEnumerable<T>, forcing subsequent query operators to bind to Enumerable operators instead of Queryable operators. This causes the remainder of the query to execute locally.

To illustrate, suppose we had a MedicalArticles table in SQL Server and wanted to use LINQ to SQL or EF to retrieve all articles on influenza whose abstract contained less than 100 words. For the latter predicate, we need a regular expression:

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");

var query = dataContext.MedicalArticles
            .Where (article => article.Topic == "influenza" &&
            wordCounter.Matches (article.Abstract).Count < 100);

The problem is that SQL Server doesn’t support regular expressions, so the LINQ-to-db providers will throw an exception, complaining that the query cannot be translated to SQL. We can solve this by querying in two steps: first retrieving all articles on influenza through a LINQ to SQL query, and then filtering locally for abstracts of less than 100 words:

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");

IEnumerable<MedicalArticle> sqlQuery = dataContext.MedicalArticles
    .Where (article => article.Topic == "influenza");

IEnumerable<MedicalArticle> localQuery = sqlQuery
    .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

With AsEnumerable, we can do the same in a single query:

var query = dataContext.MedicalArticles
      .Where (article => article.Topic == "influenza")
      .AsEnumerable()
      .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

An alternative to calling AsEnumerable is to call ToArray or ToList. The advantage of AsEnumerable is that it doesn’t force immediate query execution, nor does it create any storage structure.

Timeless
  • 7,338
  • 9
  • 60
  • 94
3

It's just a nicest and shortest way to cast to an IEnumerable. If you look at it in Reflector, you can see it does nothing except return the object as an IEnumerable.

From MSDN:

The AsEnumerable(Of TSource)(IEnumerable(Of TSource)) method has no effect other than to change the compile-time type of source from a type that implements IEnumerable(Of T) to IEnumerable(Of T) itself.

Meta-Knight
  • 17,626
  • 1
  • 48
  • 58
  • I'm not sure this is true. I believe it is to do with query implementation. – gingerbreadboy Jan 06 '10 at 15:19
  • @runrunraygun: It's there because it makes it easier to switch from a IQueryable to IEnumerable, for instance, but it generally doesn't do anything special except than cast it to IEnumerable. If you don't believe me, check in Reflector ;-) – Meta-Knight Jan 06 '10 at 15:32
  • @runrunraygun: I provided a quote from the link that YOU provided to prove this ;-) – Meta-Knight Jan 06 '10 at 15:39
  • actually there is more than that effect when used in linq-to-sql - it will execute the query. – CRice Dec 04 '14 at 01:11
3

Anonymous types are a main reason to provide these kinds of extension methods. (you cannot use anonymous types in generics parameters) But a method call can use type inference allowing you to omit specifying the type in the generic parameters.

james
  • 51
  • 1
3

If there is a method on an object that has the same name as a Linq extension method it hides the extension method. Using AsEnumerable allows you to get at the extension.

This appears to be new in SP1.

Yesterday I had a line of code that extracted member identifiers from a data table:-

var lMmIds = new List<int>(
    lDmMember.DataTable.Select(R => R.MmId)
);

which worked just fine until I installed SP1. Now it won't work unless it reads

var lMmIds = new List<int>(
    lDmMember.DataTable.AsEnumerable().Select(R => (int)((dsMtables.tbMMemberRow)R).MmId)
);

Edit: I found the real reason

It's so that you can use both remote methods (e.g. WHERE in a SQL statement) and local methods in the same linq statement. Without using AsEnumerable (i.e. just casting) it will make the query generator attempt to create an expression tree for remote execution that contains the local method. Putting AsEnumerable into the query will cause the rest of that query to be executed locally on the results of the remote query.

From https://msdn.microsoft.com/en-us/library/bb335435(v=vs.110).aspx

A Table type that represents a database table could have a Where method that takes the predicate argument as an expression tree and converts the tree to SQL for remote execution. If remote execution is not desired, for example because the predicate invokes a local method, the AsEnumerable method can be used to hide the custom methods and instead make the standard query operators available.

Richard Petheram
  • 805
  • 12
  • 16
2

As you say, if a type already implements IEnumerable<T> then there's not really any functional difference between casting to the interface or calling the AsEnumerable method.

My guess, and it's only a guess, is that calling AsEnumerable improves readability and retains the fluent signature of other LINQ extension methods:

var query = ((IEnumerable<YourType>)yourCollection).Select(x => x.YourProperty);

// vs

var query = yourCollection.AsEnumerable().Select(x => x.YourProperty);

It also allows types that don't implement IEnumerable<T> - for example, DataTable - to have their own version of the AsEnumerable extension. This allows you to continue using the same pattern in queries against those types - even though it's a different AsEnumerable method that you're calling - without needing to worry about whether or not the type really implements IEnumerable<T>.

LukeH
  • 263,068
  • 57
  • 365
  • 409
  • But those types that don't implement IEnumerable don't have access to the AsEnumerable extension method - so those types aren't included in the set of types that this question is regarding. Good point nonetheless. – Erik Forbes Jan 06 '10 at 15:37
  • @Erik: My point is that you can use *the same pattern* when querying a `DataTable`, for example, even though `DataTable` doesn't implement `IEnumerable`: `var query = yourDataTable.AsEnumerable().Select(x => x["YourColumn"])` etc. http://msdn.microsoft.com/en-us/library/system.data.datatableextensions.asenumerable.aspx – LukeH Jan 06 '10 at 15:45