代码改变世界

如何创建TextWriter的子类

2009-09-11 00:42  Jeffrey Zhao  阅读(14205)  评论(28编辑  收藏

如果您需要继承TextWriter实现自己的类型,您会怎么做?继承TextWriter不难,不过接下来,您打算覆盖(override)掉哪些方法?今天我就遇到了这样的问题。还是先来看看TextWriter的成员吧:

[Serializable]
[ComVisible(true)]
public abstract class TextWriter : MarshalByRefObject, IDisposable
{
    protected char[] CoreNewLine;
    public static readonly TextWriter Null;

    protected TextWriter();
    protected TextWriter(IFormatProvider formatProvider);

    public abstract Encoding Encoding { get; }
    public virtual IFormatProvider FormatProvider { get; }
    public virtual string NewLine { get; set; }

    public virtual void Close();
    public void Dispose();
    protected virtual void Dispose(bool disposing);
    public virtual void Flush();
    public static TextWriter Synchronized(TextWriter writer);
    public virtual void Write(bool value);
    public virtual void Write(char value);
    public virtual void Write(char[] buffer);
    public virtual void Write(decimal value);
    public virtual void Write(double value);
    public virtual void Write(float value);
    public virtual void Write(int value);
    public virtual void Write(long value);
    public virtual void Write(object value);
    public virtual void Write(string value);
    [CLSCompliant(false)]
    public virtual void Write(uint value);
    [CLSCompliant(false)]
    public virtual void Write(ulong value);
    public virtual void Write(string format, object arg0);
    public virtual void Write(string format, params object[] arg);
    public virtual void Write(char[] buffer, int index, int count);
    public virtual void Write(string format, object arg0, object arg1);
    public virtual void Write(string format, object arg0, object arg1, object arg2);
    public virtual void WriteLine();
    public virtual void WriteLine(bool value);
    public virtual void WriteLine(char value);
    public virtual void WriteLine(char[] buffer);
    public virtual void WriteLine(decimal value);
    public virtual void WriteLine(double value);
    public virtual void WriteLine(float value);
    public virtual void WriteLine(int value);
    public virtual void WriteLine(long value);
    public virtual void WriteLine(object value);
    public virtual void WriteLine(string value);
    [CLSCompliant(false)]
    public virtual void WriteLine(uint value);
    [CLSCompliant(false)]
    public virtual void WriteLine(ulong value);
    public virtual void WriteLine(string format, object arg0);
    public virtual void WriteLine(string format, params object[] arg);
    public virtual void WriteLine(char[] buffer, int index, int count);
    public virtual void WriteLine(string format, object arg0, object arg1);
    public virtual void WriteLine(string format, object arg0, object arg1, object arg2);
}

看到这么多方法,每个都是virtual的,我真怕了。正如之前讨论的那样,遇到一堆一堆的virtual方法,最终确定需要从什么地方入手实在是一件极具挑战的事情。从Reflector的观察结果发现,其中所有的方法最终都会委托给这样一个空方法:

public override void Write(char value) { }

其他所有的方法,例如Write(string)方法,都会把需要输出的内容最终委托给Write(char)方法——例如拆成一个一个字符。这种做法的性能自然是比较差的(至少要多出很多Method Call,不是吗?),因此只覆盖Write(char)方法只能保证最终成果“可以运行”,却无法保证是最优秀的结果。但是又有谁可以告诉我,究竟该怎么做呢?

无奈之下,最终还是借助于Refactor,想要观察一下.NET框架内置的一些TextWriter是如何实现的。最终比较之下,发现StringWriter是一个不错的参考。因为它够简单,并且拥有了其他TextWriter子类所“共有”的扩展方式。简单地说,所有的Write(WriteLine)方法最终被“归类”为以下三种形式:

  • 写入单个字符
  • 写入字符串
  • 写入一个字符数组的一部分

表示成代码则是:

public class MyTextWriter : TextWriter
{
    public override void Write(char value) { ... }

    public override void Write(string value) { ... }

    public override void Write(char[] buffer, int index, int count) { ... }
}

例如StringWriter会将这些内容写入至内部的StringBuilder对象中。其他如StreamWriter等TextWriter的子类也几乎都是这样,看来这就是微软认为创建TextWriter的“最佳实践”了。

值得一提的是,在TextWriter的Close和Dispose的方法都会调用GC.SuppressFinalize(this):

public class TextWriter : MarshalByRefObject, IDisposable 
{
    public virtual void Close()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    // other members
}

这意味着如果我们的Writer是在与非托管资源打交道的话,可以构造一个析构函数(Finalizer)由GC作最后一道防线。如果用户明确调用了Close或Dispose方法,则GC.SuppressFinalize(this)可以避免对象进入“析构队列”,这样对象便可以得到快速释放。但是,如果一个TextWriter的子类明确不会和非托管资源打交道的话,则GC.SuppressFinalize(this)也是一种无谓的浪费。因此,StringWriter同时还重写了TextWriter的Close方法:

public class StringWriter : TextWriter
{
    public override void Close()
    {
        this.Dispose(true);
    }

    // other members
}

当然,如果是一个“有可能”会涉及到非托管资源的TextWriter,如StreamWriter,或者是一个TextWriter的封装类,那么还是保留GC.SuppressFinalizer(this)比较妥当,毕竟进入Finalizer队列的性能开销比这GC.SuppressFinalizer的性能损耗要严重得多。