C # 3.0 ElementAt не работает для функций итератора? - PullRequest
1 голос
/ 28 марта 2009

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

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

aaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbb
...

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

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

и вызывается 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();
            }
        }
    }

1 Ответ

1 голос
/ 28 марта 2009

Да, документация 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...