10

I was reading a blog by Eric Lippert where he explained why he will almost never use arrays, and the following part got me curious:

If you are writing such an API, wrap the array in a ReadOnlyCollection and return an IEnumerable or an IList or something, but not an array. (And of course, do not simply cast the array to IEnumerable and think you’re done! That is still passing out variables; the caller can simply cast back to array! Only pass out an array if it is wrapped up by a read-only object.)

So I was messing around a bit with collections:

string[] array = new[] { "cat", "dog", "parrot" };
IEnumerable<string> test1 = array.AsEnumerable();
string[] secondArray = (string[])test1;

//array1[0] is now also "whale"
array[0] = "whale";

//11 interfaces
var iArray = array.GetType().GetInterfaces();
//Still 11 interfaces??
var iTest1 = test1.GetType().GetInterfaces();

I initialize an array and then use the AsEnumerable() method on it to convert it to an IEnumerable (or so I thought), but when I cast it back to a new array, and change a value in the original array, the values of test1 and secondArray got changed to. Apparantly I just made 2 new references to the original array, instead of creating a new IEnumerable a bit like ToArray() returns a new array.

When I compare the interfaces of the array and IEnumerable, they both have the same interfaces. Why does array have that method if it doesn't actually do anything at all? I know AsEnumerable() has its uses with Linq-to-entities to get the enumerable methods when you've got an IQueryable, but why would this method be added to an array? Is there any practical use for this method?

Edit: This comment by Tim Schmelter raises a really good point and shouldn't go unnoticed:

"It's not so useless. You can change the actual type without breaking the rest of the code. So you could replace the array with a database query or list or hashset or whatever, but the AsEnumerable always works and the rest of the code after too. So the AsEnumerable is like a contract."

Alexander Derck
  • 13,818
  • 5
  • 54
  • 76
  • maybe this can help you: http://stackoverflow.com/questions/2013846/why-use-asenumerable-rather-than-casting-to-ienumerablet – Domysee Dec 10 '15 at 13:59
  • 1
    `The AsEnumerable(IEnumerable) method has no effect other than to change the compile-time type of source from a type that implements IEnumerable to IEnumerable itself.` https://msdn.microsoft.com/library/bb335435(v=vs.100).aspx –  Dec 10 '15 at 13:59
  • And the implication of the above (what I've quoted) means you can iterate over it differently. Not sure what else it offers. –  Dec 10 '15 at 14:01
  • @JᴀʏMᴇᴇ yes, but that's what I don't get, 'change it to `IEnumerable` itself', it clearly doesn't if I look at my code... – Alexander Derck Dec 10 '15 at 14:02
  • @AlexanderDerck It changes the compile time type. It doesn't need to create a new object to do that. – Servy Dec 10 '15 at 14:03
  • @Domysee I said my question too that I know the method has its uses for Linq-to-entities/linq-to-sql but it doesn't make sense on an array? Am I missing something, or is the method there just because it's there? – Alexander Derck Dec 10 '15 at 14:03
  • 2
    `AsEnumerable()` is not a method of arrays. It's an extension method on `IEnumerable`. Because arrays happen to implement `IEnumerable` you can call it on an array. – Dennis_E Dec 10 '15 at 14:05
  • @AlexanderDerck It's not *often* useful, but it's not *entirely* useless. It is fairly rare to need it. – Servy Dec 10 '15 at 14:05
  • 2
    @AlexanderDerck the method is there because it is an extension method on IEnumerable and an array implements IEnumerable. – Domysee Dec 10 '15 at 14:05
  • I think you are missing the fact that your source is always the first array. Anything esle converted from it to anything else will be just a wrapper with extra interface. if you add anything to the source and then execute a method from a wrapper object (extention), you will get new result according to a current state of the source (your the very first array). – Celdor Dec 10 '15 at 14:07
  • @Dennis_E That explains why it's there while it looks useless, although it's not entirely useless when you have your own extension methods with the same names as existing ones. Thanks guys, looks like I will never really need it – Alexander Derck Dec 10 '15 at 14:08

3 Answers3

11

AsEnumerable is just a way of casting the value to IEnumerable<T>. It doesn't create a new object. The implementation of the method is as follows:

public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource> source)
{
    return source;
}

It's not creating a new object at all.

Reasons it's useful:

  1. If an object implements IEnumerable<T> but also has an instance method named Select, Where, etc., you can't use the LINQ method. AsEnumerable lets you cast it to IEnumerable to avoid such overload conflicts.

  2. If you want to cast an object to IEnumerable<T> (for whatever reason), but T is an anonymous type, you can't use a cast, you need a generic method that can infer its generic argument to do it.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Servy
  • 202,030
  • 26
  • 332
  • 449
6

You have a couple of good answers here; I thought I'd add a few supporting points.

First, FYI, a "do nothing" method like this that always returns its argument is called an "identity", for the obvious reason that the output is identical to the input. Identities seem useless but they actually do come in handy from time to time.

Second, if you do want to turn an array into a read-only sequence that does not have referential identity to the array, you can do an identity projection:

var sequence = from item in array select item;

or equivalently:

var sequence = array.Select(item => item);

Note that the projection here is an identity; I said they were useful.

Normally LINQ would optimize away an identity projection; if you say

from item in array where whatever select item;

then the identity projection at the end is never generated because it is just a waste of time. That is, this means

array.Where(item => whatever)

not

array.Where(item => whatever).Select(item => item)

But the compiler does not suppress the identity projection in the case where the select is the only thing in the query, precisely so that you can make a projection that cannot be cast back to the original array.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
4

The purpose of AsEnumerable is quite clear as described in the Servy's answer and this post for example: Why use .AsEnumerable() rather than casting to IEnumerable<T>?.

The remaining question is: why is it visible on IEnumerable<T>? This is simply the case because the extension method used here uses IEnumerable<T> as the type it is defined on. See it as ToString() on a string. Since string is an object it derives the method ToString(), although useless in the case of a string. Here, the extension method causes this 'useless' availability of the method.

Community
  • 1
  • 1
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • 2
    It's not so useless. You can change the actual type without breaking the rest of the code. So you could replace the array with a database query or list or hashset or whatever, but the `AsEnumerable` always works and the rest of the code after too. So the `AsEnumerable` is like a contract. – Tim Schmelter Dec 10 '15 at 14:11
  • 1
    This isn't answering the question asked, it's merely providing tangentially related information, and as such, should just be a comment. – Servy Dec 10 '15 at 14:12
  • Indeed, Servy covered that nicely. I just wanted to point out the "Why does array have that method if it doesn't actually do anything at all?" part of the question. It does have a purpose in some cases, but not all. – Patrick Hofman Dec 10 '15 at 14:12
  • Yes I noticed casting it to `IEnumerable` did the same thing as `AsEnumerable()` while I was messing around. All these extension methods can get kind of confusing though, sometimes c# gets a bit too abstract for me :p – Alexander Derck Dec 10 '15 at 14:13
  • @Servy why do you think this does not answer the question? – Patrick Hofman Dec 10 '15 at 14:14
  • 1
    @PatrickHofman Why do you think it does? The question is asking what the purpose of this method is, and why it would be useful. You merely state that I answer that question. That's not answering the question. – Servy Dec 10 '15 at 14:15