将Stream转换为IEnumerable。如果可能的话,“保持懒惰”

时间:2010-04-13 14:32:56

标签: c#

我收到了一个Stream,需要将IEnumerable传递给另一个方法。

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

一种方法是读取整个Stream,将其转换为字节数组并将其传入,因为Array实现了IEnumerable。但如果我能以这样的方式传入它,以至于在传入之前我不必阅读整个Stream,那就更好了。

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

2 个答案:

答案 0 :(得分:13)

这个按需“逐个字节”地读取你的流:

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

甚至更短,如果流为空,则不会引发异常,但只会产生任何结果:

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

答案 1 :(得分:5)

我做了一些实验并写了一些类似于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的抽象类,它在TextReader上使用IEnumerable来实现IEnumerable,这样一个简单的解析器可以做类似的事情:

foreach (Token t in stateMachine) {
}