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


我收到一个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
}

12504
2018-04-13 14:32


起源

什么期望枚举?是吗 真 想要枚举流的每个字节? - Jon Skeet
您可以创建一个包装器,包装流,提供IEnumerable支持,以您希望的方式实现。 - M.A. Hanin
@Jon Skeet:我计划枚举文件的字节然后让 anotherMethodWhichWantsAnIEnumerable 已经实施的就是它的魔力。 - Deleted
@ Binary255:奇怪...大多数需要字节序列的东西只会使用一个流...... - Jon Skeet
如果您对我们的任何答案不满意,请将其中一个答案标记为已接受的答案,或提供更多信息。 - Philip Daubmeier


答案:


这个按字节'按需'逐字节读取你的流:

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

11
2018-04-13 14:52



刚试过它并注意到了同样:)纠正了它。 - Philip Daubmeier
我假设你想要一个IEnumerable <byte>。如果你想要别的东西(例如IEnumerable <int>,IEnumerable <string>包含TextStream的所有行,...),请告诉我。 - Philip Daubmeier


我做了一些实验并写了类似于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()) { /* ... */ }

对于grins,我编写了一个名为TokenizingStateMachine的抽象类,它在TextReader上使用IEnumerable来实现IEnumerable,这样一个简单的解析器可以做类似的事情:

foreach (Token t in stateMachine) {
}

5
2018-04-13 15:20