如何创建TextWriter的子类
2009-09-11 00:42 Jeffrey Zhao 阅读(14510) 评论(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的性能损耗要严重得多。