Enumerable.ElementAt <tsource>?</tsource>的重点是什么

时间:2013-01-15 13:20:56

标签: c# .net ienumerable

IEnumerable<T>公开了一个枚举器,因此可以枚举该对象。此接口公开的索引没有任何内容。 IList<T>与索引有关,因为它公开了IndexOf方法。

那么Enumerable.ElementAt的重点是什么?我刚刚阅读了这个LINQ扩展方法的doc

  

返回序列中指定索引处的元素。

嗯,是的,这是关于序列,而不仅仅是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>接口来假设您的元素已编入索引。因此,您不希望ElementAt使用IEnumerable

在上面的例子中,我认为IsEnumerableOrdered应该需要一个IList<T>参数,因为它意味着元素是一个序列。实际上我找不到ElementAt方法有用的场景,而且不容易出错。

1 个答案:

答案 0 :(得分:5)

有许多IEnumerable类型,如数组或列表。所有IList类型(Array也实现)都有indexer,您可以使用它来访问特定索引处的元素。

如果序列可以成功转换为Enumerable.ElementAtIList将使用此功能。否则将被列举。

因此,对于所有类型的IEnumerable类型,访问给定索引处的元素只是一种方便的方法。

这样做的好处是您可以在以后更改类型,而无需更改arr[index]的所有出现。

对于它的价值,这里是反映(ILSpy)方法来证明我所说的:

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;
}