I do not think that any of the existing answers really answers the question. The actual relationship between the two classes is an example of an adapter pattern .
StringWriter implements all of its Write... methods by forwarding to the StringBuilder instance that it stores in the field. This is not just an internal detail, because StringWriter has a public GetStringBuilder method that returns an internal string builder, as well as a constructor that allows you to pass an existing StringBuilder .
So, StringWriter is an adapter that allows you to use StringBuilder as a target with code that expects to work with TextWriter . From the point of view of the basic behavior, there is clearly nothing to choose between them ... if you cannot measure the overhead of call forwarding, in which case the StringWriter will be a little slower, but this seems very unlikely.
So why didn’t they make StringBuilder implement TextWriter directly? This is a gray area because the intention of the interface is not always always clear at first glance.
TextWriter is almost an interface for something that accepts a character stream. But he has an extra wrinkle: a property called Encoding . This means that TextWriter is an interface to what accepts a stream of characters and also converts them to bytes.
This is a useless remainder in StringWriter because it does not perform encoding. The documentation says:
This property is necessary for some XML scripts where the header must be written with the encoding used by StringWriter. This allows the XML to consume an arbitrary StringWriter and generate the correct XML header.
But this may not be correct, because we do not need to specify the Encoding value for StringWriter . The property always has the value UnicodeEncoding . Therefore, any code that considered this property to build an XML header would always say utf-16 . For example:
var stringWriter = new StringWriter(); using (var xmlWriter = XmlWriter.Create(stringWriter)) xDocument.WriteTo(xmlWriter);
This creates the header:
<?xml version="1.0" encoding="utf-16"?>
What if you used File.WriteAllText to write your XML string to a file? By default, you will have a utf-8 file with the utf-16 header.
In such scenarios, it would be safer to use StreamWriter and build it using a file path or FileStream , or if you want to examine the data, use a MemoryStream and thus get an array of bytes . All of these combinations would ensure that byte encoding and header generation would be guided by the same Encoding value in your StreamWriter .
The purpose of the Encoding property was to allow character stream generators to include accurate encoding information in the character stream itself (for example, in the XML example, other examples include email headers, etc.)
But by introducing StringWriter , this connection between the creation and encoding of content is broken, and therefore such automatic mechanisms stop working and become potentially error prone.
Nevertheless, StringWriter is a useful adapter if you are careful, that is, understand that the code for generating your content should not depend on the meaningless value of the Encoding property. But such a caution is usually associated with the adapter template. This is often a kind of hack, allowing you to fit a square-but-almost round pin into a round hole.