1

While working with the System.IO.StreamReader class of the .NET Framework I noticed what seems to me to be strangely inconsistent behavior:

Calling the Read(Char[], Int32, Int32) method and passing 0 for the count parameter (i.e. read zero bytes), the Position property on the underlying Stream object is 0, as expected.

However calling the ReadAsync(Char[], Int32, Int32) method in the same way, the Position property on the underlying Stream object is not zero, as I would expected, but the length of the contents of the stream; I would expect it to be zero as in the synchronous test case.

Is this a bug in the framework implementation or am I missing something?

The following test code demonstrates:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using System.Threading.Tasks;

namespace ExplorationTesting
{

    [TestClass]
    public class TestStreamReader
    {

        public static Stream CreateStream(string fromString)
        {
            var stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            writer.Write(fromString);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }

        [TestMethod] // Test Passes
        public void TestReadingZeroBytesFromStreamReader_StreamPositionIsZero()
        {
            using (Stream stream = CreateStream("Foo"))
            using (StreamReader reader = new StreamReader(stream)) {
                Assert.AreEqual(0, stream.Position);
                char[] buffer = new char[] { };
                var r = reader.Read(buffer, 0, 0);
                Assert.AreEqual(0, r);
                Assert.AreEqual(0, stream.Position);
            }
        }

        [TestMethod] // Test Fails
        public async Task TestReadingZeroBytesFromStreamReader_StreamPositionIsZero_Async()
        {

            using (Stream stream = CreateStream("Foo"))
            using (StreamReader reader = new StreamReader(stream)) {
                Assert.AreEqual(0, stream.Position);
                char[] buffer = new char[] { };
                var r = await reader.ReadAsync(buffer, 0, 0);
                Assert.AreEqual(0, r);
                Assert.AreEqual(0, stream.Position); // Assert.AreEqual failed. Expected:<0>. Actual:<3>. 
            }
        }
    }
}
Jacek Blaszczynski
  • 3,183
  • 14
  • 25
Dan Stevens
  • 6,392
  • 10
  • 49
  • 68
  • With Async Opeartions, Avoidance of Race Conditins is paramount. So chances are it tries to move the cursor, before it even checks how much to read. | It is acttually rather wierd t hat you call read with 0 or even care about the position like this. Are you doing stuff like accessing the same stream from Multitple Tasks? Because Multiple readers or writers is not what those classes are designed for. – Christopher Nov 29 '19 at 18:27
  • 1
    Contrary to the comment from @Christopher, this has nothing to do with async per se, other than the fact that in the synchronous case, `StreamReader` chooses to not read anything, while in the async case, it goes ahead and buffers new data. Regardless of async or not, `StreamReader` is buffered and always may or may not read a number of bytes that correlates with the number of characters you ask for. See marked duplicates. – Peter Duniho Nov 29 '19 at 19:42
  • 1
    See also related Q&A such as https://stackoverflow.com/questions/520722/unbuffered-streamreader and https://stackoverflow.com/questions/737408/bytes-consumed-by-streamreader – Peter Duniho Nov 29 '19 at 19:43
  • 1
    It seems that currently first call to ReadAsync will always result in [ReadBufferAsync](https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/StreamReader.cs#L1221) being called at https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/StreamReader.cs#L991. And then it will always fill the buffer(read the internal stream) either at https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/StreamReader.cs#L1238 or https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/StreamReader.cs#L1260 – Eugene Podskal Nov 29 '19 at 20:10

0 Answers0