IEnumerable<T>
exposes an enumerator, so the object can be enumerated. There is nothing about indexes exposed by this interface. IList<T>
is about indexes, as it exposes the IndexOf
method.
So what's the point of Enumerable.ElementAt? I just read the doc of this LINQ extension method:
Returns the element at a specified index in a sequence.
Well, yes, it's about a sequence, not just an IEnumerable
. Reading the remarks:
If the type of source implements IList, that implementation is used to obtain the element at the specified index. Otherwise, this method obtains the specified element.
Okay, so if the concrete type implements something that inherits from IList<T>
(which is an actual sequence), then it's the same as IndexOf()
. If not, it iterates until the index is reached.
Here's a sample scenario:
// Some extension method exposed by a lib
// I know it's not a good piece of code, but let's say it's coded this way:
public static class EnumerableExtensions
{
// Returns true if all elements are ordered
public static bool IsEnumerableOrdered(this IEnumerable<int> value)
{
// Iterates over elements using an index
for (int i = 0; i < value.Count() - 1; i++)
{
if (value.ElementAt(i) > value.ElementAt(i + 1))
{
return false;
}
}
return true;
}
}
// Here's a collection that is enumerable, but doesn't always returns
// its objects in the same order
public class RandomAccessEnumerable<T> : IEnumerable<T>
{
private List<T> innerList;
private static Random rnd = new Random();
public RandomAccessEnumerable(IEnumerable<T> list)
{
innerList = list.ToList();
}
public IEnumerator<T> GetEnumerator()
{
var listCount = this.innerList.Count;
List<int> enumeratedIndexes = new List<int>();
for (int i = 0; i < listCount; i++)
{
int randomIndex = -1;
while (randomIndex < 0 || enumeratedIndexes.Contains(randomIndex))
{
randomIndex = rnd.Next(listCount);
}
enumeratedIndexes.Add(randomIndex);
yield return this.innerList[randomIndex];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
// Here's some test program
internal class Program
{
private static void Main()
{
var test0 = new List<int> { 0, 1, 2, 3 };
var test1 = new RandomAccessEnumerable<int>(test0);
Console.WriteLine("With List");
Console.WriteLine(test0.IsEnumerableOrdered()); // true
Console.WriteLine(test0.IsEnumerableOrdered()); // true
Console.WriteLine(test0.IsEnumerableOrdered()); // true
Console.WriteLine(test0.IsEnumerableOrdered()); // true
Console.WriteLine(test0.IsEnumerableOrdered()); // true
Console.WriteLine("With RandomAccessEnumerable");
Console.WriteLine(test1.IsEnumerableOrdered()); // might be true or false
Console.WriteLine(test1.IsEnumerableOrdered()); // might be true or false
Console.WriteLine(test1.IsEnumerableOrdered()); // might be true or false
Console.WriteLine(test1.IsEnumerableOrdered()); // might be true or false
Console.WriteLine(test1.IsEnumerableOrdered()); // might be true or false
Console.Read();
}
}
So, as RandomAccessEnumerable
might return enumerated objects in a random order, you just can't rely on the simple IEnumerable<T>
interface to assume your elements are indexed. So you don't want to use ElementAt
for an IEnumerable
.
In the above example, I think IsEnumerableOrdered
should require a IList<T>
parameter as it implies elements are a sequence. I actually can't find a scenario where the ElementAt
method is useful, and not bug-prone.
ElementAt() is a System. Linq method in C# that is used to get and display element at a particular index. The following is our string array − string[] arr = { "One", "Two", "Three", "Four", "Five" }; Now to get an element at index 0, use the ElementAt() method − arr.ElementAt(0); The following is the complete code −
IEnumerable interface is a generic interface which allows looping over generic or non-generic lists. IEnumerable interface also works with linq query expression. IEnumerable interface Returns an enumerator that iterates through the collection.
The IndexOf method returns the first index of an item if found in the List. C# List<T> class provides methods and properties to create a list of objects (classes). The IndexOf method returns the first index of an item if found in the List.
There are many IEnumerable
types like array or list. All IList
types(which Array
also implements) have an indexer which you can use to access elements at a specific index.
This will be used by Enumerable.ElementAt
if the sequence can be casted to IList
successfully. Otherwise it will be enumerated.
So it's just a convenient way to access elements at a given index for all kind of IEnumerable
types.
This has the advantage that you can change the type later without needing to change all occurences of arr[index]
.
For what it's worth, here's the reflected(ILSpy) method to demonstrate what i've said:
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
return list[index];
}
if (index < 0)
{
throw Error.ArgumentOutOfRange("index");
}
TSource current;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
if (index == 0)
{
current = enumerator.Current;
return current;
}
index--;
}
throw Error.ArgumentOutOfRange("index");
}
return current;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With