Friday, June 05, 2015

C#: How to Record What Gets Written to or Read From a Stream

Streams are a very nice abstraction over a read/write loop. We can use them to represent the contents of a file, or a stream of bytes to or from a network socket. They make it easy to read and write large amounts of data without consuming large amounts of memory. Take this little code snippet:

Example.txt may be many GB in size, but this operation will only ever use the amount of memory configured for the buffer. As an aside, the .NET framework’s Stream class’s default buffer size is the maximum multiple of 4096 that is still smaller than the large object heap threshold (85K). This means it likely to be collected at gen zero by the garbage collector, but still gives good performance.

But what if we want to log or view the contents of Example.txt as it’s copied to the output file? Let me introduce my new invention: InterceptionStream. This is simple class that inherits and decorates Stream and takes an additional output stream. Each time the wrapped stream is read from, or written to, the additional output stream gets the same information written to it. You can use it like this:

I could just as well have wrapped the input stream with the InterceptionStream for the same result:

You can use a MemoryStream if you want to capture the log in memory and assign it to a string variable, but of course this negates the memory advantages of the stream copy since we’re now buffering the entire contents of the stream in memory:

Here is the InterceptionStream implementation. As you can see it’s very simple. All the work happens in the Read and Write methods:

5 comments:

Павел Мартынов said...

Another useful case for decorating Stream is throughput measurement, especially helpful with network streams.

trailmax said...

That's pretty neat! Never had a need for stream decorator, but I'll remember for future cases.

Anonymous said...

Embarrassingly, what I learnt first from this post is that you can stack using declarations on top of each other and then just open/close curly braces once! :D

Rutland Gizz said...

/\/\ What Adam said! :)

Anonymous said...

re. stacked usings, the same applies to any statements where curly braces are optional, so you can have:

while (true)
using (var x = "blah")
if (i > t)
{ ... }