Является ли C# 3.0 ElementAt неработающим для функций итератора?

StackOverflow https://stackoverflow.com/questions/691889

  •  22-08-2019
  •  | 
  •  

Вопрос

Может ли кто-нибудь сказать мне, почему следующий код не работает так, как я ожидаю?Я пытаюсь написать оболочку IEnumberable вокруг StreamReader, но когда я использую ElementAt, он считывает последовательные символы из потока независимо от индекса, который я передаю ElementAt.

Файл «test.txt» содержит «abcdefghijklmnopqrstuvwxyz».Я ожидаю, что результат будет:

аааааааааааааааааааааааа
ббббббббббббббббббббббббббббб
...

Вместо этого я получаю

АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЫЭЮЯ

и вызывается ArgumentOutOfRangeException, хотя единственный индекс, который я передал ElementAt, равен 0.

MSDN говорит:

Если тип источника реализует IList<(Of <(T>)>), эта реализация используется для получения элемента по указанному индексу.В противном случае этот метод получает указанный элемент.

Если это будет читаться как «получает следующий элемент"?Если так, то это противоречит цели ленивых списков...

    static IEnumerable<char> StreamOfChars(StreamReader sr)
    {
        while (!sr.EndOfStream)
            yield return (char)sr.Read();
    }


    static void Main(string[] args)
    {
        using (StreamReader sr = new StreamReader("test.txt"))
        {
            IEnumerable<char> iec = StreamOfChars(sr);

            for (int i = 0; i < 26; ++i)
            {
                for (int j = 0; j < 27; ++j)
                {
                    char ch = iec.ElementAt(i);
                    Console.Write(ch);
                }
                Console.WriteLine();
            }
        }
    }
Это было полезно?

Решение

Да, в документации MSDN должно быть написано «получает следующий элемент».Очевидно, ваш iec объект не реализуется IList и только реализует IEnumerable, поэтому невозможно выполнить случайное чтение (конечно, без метода расширения или чего-то еще, что использует перечислитель для достижения случайного индекса).Только прямое чтение — единственный доступный вариант.

Давайте посмотрим на дизассемблерный код метода ElementAt.Очевидно, что если исходная коллекция не реализует IList, мы получаем текущий элемент в перечислителе:

public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index)
{
    TSource current;
    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");
    }
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
    Label_0036:
        if (!enumerator.MoveNext())
        {
            throw Error.ArgumentOutOfRange("index");
        }
        if (index == 0)
        {
            current = enumerator.Current;
        }
        else
        {
            index--;
            goto Label_0036;
        }
    }
    return current;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top