Ответ 1
Я не думаю, что любой из существующих ответов действительно отвечает на вопрос. Фактическая взаимосвязь между двумя классами является примером шаблона адаптера.
StringWriter
реализует все свои методы Write...
путем пересылки на экземпляр StringBuilder
, который он хранит в поле. Это не просто внутренняя деталь, потому что StringWriter
имеет открытый метод GetStringBuilder, который возвращает внутренний построитель строк, а также конструктор, который позволяет вам передать существующий StringBuilder
.
Итак, StringWriter
- это адаптер, который позволяет использовать StringBuilder
в качестве цели кодом, который ожидает работать с TextWriter
. С точки зрения базового поведения явно нечего выбирать между ними... если вы не можете измерить накладные расходы на переадресацию вызовов, и в этом случае StringWriter
будет немного медленнее, но это кажется очень маловероятным.
Так почему же они не сделали StringBuilder
реализовать TextWriter
напрямую? Это серая область, потому что намерение интерфейса не обязательно всегда ясно на первый взгляд.
TextWriter
- это почти интерфейс для чего-то, что принимает поток символов. Но у него есть дополнительная морщина: свойство, называемое Encoding. Это означает, что TextWriter
является интерфейсом к тому, что принимает поток символов, а также преобразует их в байты.
Это бесполезный остаток в StringWriter
, потому что он не выполняет кодировку. В документации говорится:
Это свойство необходимо для некоторых сценариев XML, где заголовок должен записываться с кодировкой, используемой StringWriter. Эта позволяет XML-коду потреблять произвольный StringWriter и генерировать правильный XML-заголовок.
Но это не может быть правильно, потому что нам не нужно указывать значение Encoding
для StringWriter
. Свойство всегда имеет значение UnicodeEncoding
. Следовательно, любой код, который рассматривал это свойство для построения заголовка XML, всегда говорил бы utf-16
. Например:
var stringWriter = new StringWriter();
using (var xmlWriter = XmlWriter.Create(stringWriter))
xDocument.WriteTo(xmlWriter);
Это создает заголовок:
<?xml version="1.0" encoding="utf-16"?>
Что делать, если вы использовали File.WriteAllText для записи вашей XML-строки в файл? По умолчанию у вас будет файл utf-8
с заголовком utf-16
.
В таких сценариях было бы безопаснее использовать StreamWriter
и построить его с помощью пути к файлу или FileStream
, или если вы хотите изучить данные, используйте MemoryStream
и, таким образом, получить массив байтов. Все эти комбинации гарантировали бы, что кодирование в байтах и генерация заголовка будет руководствоваться одним и тем же значением Encoding
в вашем StreamWriter
.
Цель свойства Encoding
заключалась в том, чтобы позволить генераторам потоков символов включать точную информацию о кодировании в сам поток символов (например, в примере XML, другие примеры включают заголовки электронной почты и т.д.)
Но, введя StringWriter
, эта связь между созданием и кодированием контента нарушена, и поэтому такие автоматические механизмы перестают работать и становятся потенциально подверженными ошибкам.
Тем не менее, StringWriter
является полезным адаптером, если вы осторожны, то есть понимаете, что код генерации вашего контента не должен зависеть от бессмысленного значения свойства Encoding
. Но такое предостережение обычно связано с шаблоном адаптера. Это часто своего рода хак, позволяющий вам приспосабливать квадратный-но-почти круглый штырь в круглое отверстие.