IEnumerable<T>
暴露了一个枚举器,因此可以枚举对象。这个接口没有关于索引的任何信息。而IList<T>
是关于索引的,因为它公开了IndexOf
方法。
那么Enumerable.ElementAt有什么用呢?我刚刚读了这个LINQ扩展方法的文档:
返回序列中指定索引处的元素。
好的,是关于序列的,不仅仅是IEnumerable
。继续阅读备注:
如果源类型实现了IList,则使用该实现来获取指定索引处的元素。否则,此方法获取指定的元素。
好的,所以如果具体类型实现了从IList<T>
继承的东西(这是一个真正的序列),那么它就与IndexOf()
相同。如果没有,它就会迭代直到达到索引。
下面是一个示例场景:
// 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();
}
}
因此,由于
RandomAccessEnumerable
可能以随机顺序返回枚举对象,您不能仅依赖简单的 IEnumerable <T>
接口来假定您的元素被索引。因此,您不想使用 IEnumerable
中的 ElementAt
。在上面的示例中,我认为
IsEnumerableOrdered
应该需要一个 IList<T>
参数,因为它暗示元素是一个序列。实际上,我找不到使用 ElementAt
方法有用且不易出错的情况。
IOrderedEnumerable<T>
类型。 - jrummellLinkedList
是一个序列,但它没有实现IList
:( - tukaefElementAt
不当的代码,以及一个在许多Enumerable
方法中提供非确定性结果的IEnumerable
,并不意味着ElementAt
是无用或容易出错的。 - RawlingIEnumerable
,那么你就会面临各种各样的未知情况。例如,是否可以安全地多次枚举它?因此,你需要记录你的库方法的期望,并要求使用你的库的人满足这些期望。 - Damien_The_Unbeliever