传递一个 "AppendOnlyStringBuilder"

Pass around an "AppendOnlyStringBuilder"

我有一个序列化任务,其中我的对象模型的不同部分和部分知道如何发出它们自己的字符串段,但我希望可以选择使用 .Append 来发出它们而不是常见的 StringBuilder 对象。根据我们不应该将我们的私有对象传递给其他人随意使用的理论,这里有一个 class 包装了一个 StringBuilder 以使其 "append-only" 在我的场景中使用。

public sealed class AppendOnlyStringBuilder {
   private readonly StringBuilder _stringBuilder;

   public AppendOnlyStringBuilder(StringBuilder stringBuilder)
   {
      _stringBuilder = stringBuilder;
   }

   public AppendOnlyStringBuilder Append<T>(T value)
   {
      _stringBuilder.Append(value);
      return this;
   }
}

如果我的模型不同块中的代码现在看起来都与此类似:

// Chunk1 class
public override string ToString() {
   StringBuilder sb = new StringBuilder();
   sb
      .Append(_prop1.Value)
      .Append(_prop2.Value)
      .Append(_prop3.Value);
   return sb.ToString();
}

这些 ToString 方法是从主例程的序列化方法中调用的,如下所示:

// MainObject class
Chunk1 _chunk1;
Chunk2 _chunk2;
Chunk3 _chunk3;

public override string ToString() {
   StringBuilder sb = new StringBuilder();
   sb
      .Append(_chunk1.ToString()) // ToString may be unnecessary here
      .Append(_chunk2.ToString())
      .Append(_chunk3.ToString());
   return sb.ToString();
}

我如何优雅地切换到传递单个 AppendOnlyStringBuilder 以用于所有这些块 classes 而不是每个块都在内部创建一个新的 StringBuilder 并执行 ToString?

我希望它像这样使用:

// MainObject class
public override string ToString() {
   StringBuilder sb = new StringBuilder();
   AppendOnlyStringBuilder aosb = new AppendOnlyStringBuilder(sb);
   aosb
      .Append(_chunk1)
      .Append(_chunk2)
      .Append(_chunk3);
   return sb.ToString();
}

扩展方法是获得此语法的自然方式,但我 运行 遇到了问题,因为扩展方法需要是静态的,因此无法访问块的私有部分 classes。我想在块 classes 中保留相同的 ToString 这样他们仍然可以做他们正常的 "serialize just me" 事情,但我也希望他们能够有选择地追加到我共同的 AppendOnlyStringBuilder.

我想我可以做到:

_chunk1.AppendTo(aosb);
_chunk2.AppendTo(aosb);
_chunk3.AppendTo(aosb);

但是这让我有些烦恼——我想使用一个以 AppendOnlyStringBuilder 对象开头的流畅界面,就像我之前的例子一样。

如果你只想要一个类型的实例化对象,你可以使用单例模式(Google 会给你很多例子)

如果您有一个全局对象(类似于 "myApplication"-class),您可以在那里提供它。

其他方式是静态 class 提供此单例实例 "from everywhere"。

您的代码将直接附加到这个实例...

希望对您有所帮助

好的,开始了:

using System.IO;

abstract class BaseClass
{
    protected abstract void WriteToTextWriter(TextWriter textWriter);

    public void SerializeTo(TextWriter textWriter)
    {
        WriteToTextWriter(textWriter);
    }

    public sealed override string ToString()
    {
        var writer = new StringWriter();
        SerializeTo(writer);
        return writer.ToString();
    }
}

abstract class ChunkBase : BaseClass
{
    private readonly string _index;

    protected ChunkBase(string index)
    {
        _index = index;
    }

    protected sealed override void WriteToTextWriter(TextWriter textWriter)
    {
        textWriter.Write("Chunk");
        textWriter.Write(_index);
    }
}

class Chunk1 : ChunkBase { public Chunk1() : base("1") { } }
class Chunk2 : ChunkBase { public Chunk2() : base("2") { } }
class Chunk3 : ChunkBase { public Chunk3() : base("3") { } }

class ClassWithChunks : BaseClass
{
    private readonly Chunk1 _chunk1 = new Chunk1();
    private readonly Chunk2 _chunk2 = new Chunk2();
    private readonly Chunk3 _chunk3 = new Chunk3();

    protected override void WriteToTextWriter(TextWriter textWriter)
    {
        _chunk1.SerializeTo(textWriter);
        _chunk2.SerializeTo(textWriter);
        _chunk3.SerializeTo(textWriter);
    }
}

现在,如果你想链接,你可以这样做:

class Chainable
{
    private readonly TextWriter _textWriter;

    public Chainable(TextWriter textWriter)
    {
        _textWriter = textWriter;
    }

    public Chainable Write(BaseClass obj)
    {
        obj.SerializeTo(_textWriter);
        return this;
    }
}

然后,您的 WriteToTextWriter 可能是这样的:

public override void WriteToTextWriter(TextWriter textWriter)
{
    new Chainable(textWriter)
        .Write(_chunk1)
        .Write(_chunk2)
        .Write(_chunk3);
}

我不确定它是否值得:代码当然更清晰,但由于额外的复杂层,某人(包括你未来的自己)将更难破译。

编辑:使抽象方法受保护在这里似乎没什么用,但在生产代码中,额外的层可能会有帮助。您还需要添加一些开关来处理格式化等。

似乎是适配器模式的理想候选者。 (未经测试的代码)

public interface IAppendOnly
{
  void Append(string content);
}

public class AppendOnlyStringBuilder : IAppendOnly
{
  private StringBuilder _stringBuilder = new StringBuilder()

  public void Append(string content)
  {
    _stringBuilder.Append(content);
  }

  public override string ToString()
  {
    return _stringBuilder.ToString();
  }
}

public class Chunk
{
  public void AppendTo(IAppendOnly appendOnly)
  {
    appendOnly.Append("My Content");
  }
}

然后每个块在不知道接口如何实例化的情况下工作:

_chunk1.AppendTo(aosb);
_chunk2.AppendTo(aosb);
_chunk3.AppendTo(aosb);

But something about that bugs me--I'd like to use a fluent interface that begins with the AppendOnlyStringBuilder object as in my example previously.

因此,根据这个要求(减去不必要的 AppendOnlyStringBuilder class),您将切换界面方向。

public interface IGetString
{
  string GetString();
}

public Chunk : IGetString
{
  public string GetString()
  {
    return "MyContent";
  }
}

public static class StringBuilderExtensions
{
  public static StringBuilder AppendFrom(this StringBuilder instance
    , IGetString getString)
  {
    instance.Append(getString.GetString())
    return instance;
  }
}

那就是流利了:

var sb = new StringBuilder;
var a = new Chunk();
var b = new Chunk();

sb.AppendFrom(a).AppendFrom(b);