IEnumerable<T>
выдает перечислитель, поэтому объект можно перечислить. Нет ничего об индексах, открытых этим интерфейсом. IList<T>
относится к индексам, поскольку предоставляет метод IndexOf
.
Итак, какова точка Enumerable.ElementAt? Я просто прочитал doc этого метода расширения LINQ:
Возвращает элемент по указанному индексу в последовательности.
Ну, да, это примерно последовательность, а не только IEnumerable
. Чтение замечаний:
Если тип источника реализует IList, эта реализация используется для получения элемента по указанному индексу. В противном случае этот метод получает указанный элемент.
Хорошо, поэтому, если конкретный тип реализует что-то, что наследует от IList<T>
(которое является фактической последовательностью), то оно равно, как IndexOf()
. Если нет, он выполняет итерацию до достижения индекса.
Вот пример сценария:
// Some extension method exposed by a lib
// I know it not a good piece of code, but let say it 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 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 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();
}
}
Итак, поскольку RandomAccessEnumerable
может возвращать перечисленные объекты в случайном порядке, вы просто не можете полагаться на простой интерфейс IEnumerable<T>
, чтобы предположить, что ваши элементы индексированы. Поэтому вы не хотите использовать ElementAt
для IEnumerable
.
В приведенном выше примере я думаю, что IsEnumerableOrdered
должен требовать параметр IList<T>
, поскольку он подразумевает, что элементы являются последовательностью. Я действительно не могу найти сценарий, в котором метод ElementAt
полезен, а не подвержен ошибкам.