Question

Let me first apologize if this question could sound perhaps sort of amateurish for the seasoned programmers among you, the thing is I've been having many arguments about this at work so I really want to get this straight and that's basically why I'm relying on the stackoverflow community to get this settled once and for all :)

So, on the one hand according to MSDN, we have:

TextWriter Class

Represents a writer that can write a sequential series of characters. This class is abstract.

FileStream Class

Exposes a Stream around a file, supporting both synchronous and asynchronous read and write operations.

StreamWriter Class

Implements a TextWriter for writing characters to a stream in a particular encoding.

On the other hand it's evident they all belong to System.IO but given that MSDN examples kind of mix some of them, I'm still not reaching the much desired a-ha moment.

Any comment would be more than appreciated, thanks much in advance!

Was it helpful?

Solution

Streams handle bytes, Writers handle characters.

Bytes != characters. A character may take more than one byte to represent. The mapping from characters to bytes is called an encoding.

A FileStream refers to the bytes being written to a file, similar to how a MemoryStream refers to the bytes written to an in-memory buffer. In order to write characters to a stream, you'd need to convert them to a string of bytes. That's where a StreamWriter comes in to play. It takes a sequence of characters and an encoding, and turns it into a sequence of bytes.

A TextWriter is an interface (well, abstract base class) that all of the Writers must adhere to. It has all operations based on characters. The equivalent for bytes is the Stream abstract base class.

Things also go in the opposite direction. There is a TextReader abstract base class, describing how to read characters from somewhere, and a StreamReader, which allows you to read characters from a byte-oriented stream supplying an encoding - but this time used in reverse, to aggregate any multi-byte sequences into single characters where appropriate.

A Stream can be used for both reading and writing, since bytes are the lowest-level items used in I/O operations.

OTHER TIPS

I've always found the best thing to do is just look at what methods they provide and how you can build them. This is almost always the main, if not only, thing I care about when using an API. How do I build it and what can it do?

You can't instantiate a TextWriter. It's abstract. That tells me the only real purpose it serves is, well, abstraction. If you write a function that takes any kind of writer as an argument, there's a good chance you should just take TextWriter to be more versatile.

A StreamWriter you can instantiate, and it does just what it says, it writes to streams. That means it will need a stream to get any real writing done. Once you have that stream though, you can do all sorts of neat things like writing a whole line at once instead of having to deal with individual characters (or rather bytes) like you would directly on the stream.

So basically, you get a stream so you can feed it to a StreamWriter (or Reader). If you're writing text, you probably don't want to work directly with a stream, no more than you want to work with a character array instead of a string.

FileStreams can conveniently be instantiated directly from the File and FileInfo classes, and in my usage, this is how they're usually instantiated. Get a file (I like to use FileInfo) and call OpenWrite(). Pass it along to a StreamWriter (which is just a type of TextWriter) and you're on your way.

To generalize: When you want to figure out a class, try looking at how you instantiate it and what it can do. This usually clears up a lot.

There's an obvious difference between a "Stream" and a "Writer/Reader".

A stream is a byte level representation, and is really an abstract concept that can be implemented in a variety of ways. For example, you have a FileStream and a MemoryStream. Both those are streams of bytes, but they are stored differently.

Writers and Readers give you a way to process streams, adding and extracting data from them.

For your particular examples, TextWriter is an abstract class that writes characters to a stream sequentially. It has several implementations (StreamWriter, StringWriter) that do are useful in different contexts. You would use whichever makes sense at the time. For several APIs however, all that is needed is a TextWriter, or something to call "Write" or "WriteLine" on. It isn't a concern of those APIs if your writer is used to put stuff into a string, some arbitrary memory, or a file.

The FileStream class manages getting a handle to a file and opening it for reading or writing and other filesystem functions. BinaryWriter writes binary data to a stream and StreamWriter writes character data to a stream. They both can use a FileStream object to write binary or character data to files.

TextWriter is the base class that StreamWriter inherits from. A TextWriter is intended to take a type and output a string using its Write method. StreamWriter's implementation of the TextWriter.Write method writes a string, or character data, to a stream. BinaryWriter does not inherit TextWriter because it does not write character data to a stream.

Stream is an abstract base class that represents a series of bytes.

  • MemoryStream is a stream of bytes held in memory, backed by an Array.

  • FileStream is a stream of bytes in a file, usually backed by a file handle somewhere on disk.

Text characters are themselves composed of bytes, and a single character can be multiple bytes, depending on the encoding. There are some standard classes that read and write text to different sources using a specific encoding.

TextWriter is an abstract base class for writing text characters to a destination.

  • StreamWriter writes text characters (converted to bytes) to a stream of bytes.
  • StringWriter writes text characters to a string (via a StringBuilder).

TextReader is an abstract base class for reading text characters from a source.

  • StreamReader reads text characters (converted from bytes) from a stream of bytes.
  • StringReader reads text characters from a string.

Stream, TextWriter, TextReader are all abstract base classes so they are never used directly but through an implementation like the ones described above. However you will see the base classes in method definitions so that different implementations can be used, including your own custom ones if necessary. Abstract classes are similar to interfaces but can actually define the logic for methods, which can be reused without every implementation repeating the same basic code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top