Преобразование потока в IEnumerable. Если возможно, когда "держат лень",

Я получаю поток и должен передать IEnumerable другому методу.

public static void streamPairSwitchCipher(Stream someStream)
{
    ...
    someStreamAsIEnumerable = ...
    IEnumerable returned = anotherMethodWhichWantsAnIEnumerable(someStreamAsIEnumerable);
    ...
}

Один из способов - прочитать весь поток, преобразовать его в массив байтов и передать его, поскольку Array реализует IEnumerable. Но было бы намного лучше, если бы я мог пройти в нем таким образом, что мне не нужно читать весь поток, прежде чем передавать его.

public static IEnumerable<T> anotherMethodWhichWantsAnIEnumerable<T>(IEnumerable<T> p) {
    ... // Something uninteresting
}

Ответ 1

Этот читает ваш байт потока байтом "по запросу":

public static IEnumerable<byte> streamAsIEnumerable(Stream stream)
{
    if (stream == null)
        throw new ArgumentNullException("stream");

    for (; ; )
    {
        int readbyte = stream.ReadByte();
        if (readbyte == -1)
            yield break;
        yield return (byte)readbyte;
    }
}

Или даже короче и не генерирует исключение, если поток имеет значение null, но ничего не дает:

public static IEnumerable<byte> streamAsIEnumerable(Stream stream)
{
    if (stream != null)
        for (int i = stream.ReadByte(); i != -1; i = stream.ReadByte())
            yield return (byte)i;
}

Ответ 2

Я сделал несколько экспериментов по этому поводу и написал что-то похожее на phild:

public static class ExtensionMethods
{
    public static IEnumerable<byte> Bytes(this Stream stm)
    {
        while (true)
        {
            int c = stm.ReadByte();
            if (c < 0)
                yield break;
            yield return (byte)c;
        }
    }

    public static IEnumerable<char> Chars(this TextReader reader)
    {
        while (true)
        {
            int c = reader.Read();
            if (c < 0)
                yield break;
            yield return (char)c;
        }
    }
}

Разница здесь в том, что я добавил Bytes и Chars в Stream как метод расширения, который позволяет мне написать что-то вроде этого:

foreach (char c in Console.In.Chars()) { /* ... */ }

И для усмешек я написал абстрактный класс под названием TokenizingStateMachine, который использует IEnumerable в TextReader для реализации IEnumerable, так что простой парсер может делать что-то вроде:

foreach (Token t in stateMachine) {
}