Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the use of AsEnumerable() on an array?

Tags:

c#

collections

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."

like image 441
Alexander Derck Avatar asked Dec 10 '15 13:12

Alexander Derck


1 Answers

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.

like image 108
Eric Lippert Avatar answered Sep 30 '22 14:09

Eric Lippert