Mono源代码学习笔记:Console类(四)

NullStream 类 (internal class)

下面就是 mcs/class/corlib/System.IO/NullStream.cs:

01:  namespace System.IO
02:  {
03:    class NullStream : Stream
04:    {
05:      public override bool CanRead { get { return true; } }
06:      public override bool CanSeek { get { return true; } }
07:      public override bool CanWrite { get { return true; } }
08:      public override long Length { get { return 0; } }
09:      public override long Position { get { return 0; } set { } }
10:      public override void Flush() { }
11:      public override int Read(byte[] buffer, int offset, int count) { return 0; }
12:      public override int ReadByte() { return -1; }
13:      public override long Seek(long offset, SeekOrigin origin) { return 0; }
14:      public override void SetLength(long value) { }
15:      public override void Write(byte[] buffer, int offset, int count) { }
16:      public override void WriteByte(byte value) { }
17:    }
18:  }

上述源程序定义了 NullStream 类。我在我个系列学习笔记第一篇中谈到,这个 NullStream 类是从 Stream.cs 中分离出来的,经过我的整理后就变成上面这个样子。从上述的源程序中可以看出:

  1. NullStream 类位于 System.IO 命名空间中。(第 1 行)
  2. NullStream 继承自抽象基类 Stream。(第 3 行)
  3. NullStream 是个 internal 类,只能用在本程序集中。(第 3 行)
  4. NullStream 有 12 个成员,全部是公共成员,用于重写抽象基类 Stream 的虚成员。(5 - 16 行)
  5. NullStream 有 5 个属性。这些属性的 get 方法仅返回零或者 true,仅有的一个 set 方法是空的。(5 - 9行)
  6. NullStream 有 7 个方法,这方法要么是空的,要么仅返回零或者 -1。(10 – 16 行)

NullStream 类实践了 Null Object 设计模式。

由于 NullStream 类是作为 Null Object 使用的,它可以不重写抽象基类 Stream 的 ReadByte 方法(第 12 行)和 WriteByte 方法(第
16 行),因为 Stream 的这两个方法都是虚(virtual)方法,而不是抽象(abstract)方法。下面就是 Mono 源代码中的 mcs/class/corlib/System.IO/Stream.cs 文件中 Stream 类的这两个虚方法的代码:

01:  public virtual int ReadByte()
02:  {
03:    byte[] buffer = new byte[1];
04:    if (Read(buffer, 0, 1) == 1) return buffer[0];
05:    return -1;
06:  }
07:
08:  public virtual void WriteByte(byte value)
09:  {
10:    byte[] buffer = new byte[1];
11:    buffer[0] = value;
12:    Write(buffer, 0, 1);
13:  }

可以看出,Stream 类的 ReadByte 方法创建一个新的单字节数组,然后调用 Read 方法(第 3 行到第 5 行)。Stream 类的 WriteByte 方法创建一个新的单字节数组,然后调用 Write 方法(第 10 行到第 12 行)。也就是说,如果 NullStream 类不重写抽象基类 Stream 的 ReadByte 方法和 WriteByte 方法,得到的运行结果也是一样的。但是,这样效率就低了很多。

在 Console.dll 项目中,NullStream 类仅在 Console.cs 中被使用过一次。

对 NullStream 类的改进建议

我建议将 mcs/class/corlib/System.IO/NullStream.cs 改为如下所示:

01:  namespace System.IO
02:  {
03:    partial class Stream
04:    {
05:      private sealed class NullStream : Stream
06:      {
07:        public override bool CanRead { get { return true; } }
08:        public override bool CanSeek { get { return true; } }
09:        public override bool CanWrite { get { return true; } }
10:        public override long Length { get { return 0; } }
11:        public override long Position { get { return 0; } set { } }
12:        public override void Flush() { }
13:        public override int Read(byte[] buffer, int offset, int count) { return 0; }
14:        public override int ReadByte() { return -1; }
15:        public override long Seek(long offset, SeekOrigin origin) { return 0; }
16:        public override void SetLength(long value) { }
17:        public override void Write(byte[] buffer, int offset, int count) { }
18:        public override void WriteByte(byte value) { }
19:      }
20:    }
21:  }

也就是说,将 NullStream 类从 System.IO 命名空间中的 internal 类更改为 System.IO.Stream 类的私有(private)密封(sealed)内嵌类(请参阅上述源程序第 5 行)。而 mcs/class/corlib/System.IO/Stream.cs 只需作如下改动:

01:  namespace System.IO
02:  {
03:    [Serializable]
04:    [ComVisible(true)]
05:    public abstract partial class Stream : MarshalByRefObject, IDisposable
06:    {
07:      public static readonly Stream Null = new NullStream();
08:
09:      //
10:      // 这里省略 Stream 类的其余成员
11:      //
12:    }
13:  }

如上述源程序第 5 行所示,加上 partial 关键字就行了。

第 7 行通过 Stream 类的 Null 公共只读静态字段对外公开了 NullStream 类。注意,这是 Stream.cs 中原来的代码,我没有作任何改动。

现在,其他程序要使用 NullStream 类,只有通过 Stream.Null 字段来使用了,这也是 NullStream 类的唯一实例。在我们的 Console.cs 中,原来是通过 new NullStream()
来使用 NullStream 类的,现在需要改为 Stream.Null 了。

这样做的好处是显而易见的:

  1. 我们对外隐藏了 NullStream 类,大家只需要知道 Stream.Null 这个公共只读静态字段好了。
  2. NullStream 类只被实例化一次,提高了效率。
  3. NullStream 类被声明为 sealed 的,可能对 C# 编译器以及 JIT 优化调用该类的虚方法的语句有好处。例如,有可能使用 call 指令代替 callvirt 指令。

这也实践了 Singleton 设计模式和 Null Object 设计模式。

UnexceptionalStreamReader 类 (internal class)

下面就是 mcs/class/corlib/System.IO/UnexceptionalStreamReader.cs:

001:  //
002:  // System.IO.UnexceptionalStreamReader.cs
003:  //
004:  // Authors:
005:  //   Dietmar Maurer ([email protected])
006:  //   Miguel de Icaza ([email protected])
007:  //   Dick Porter (dick[email protected])
008:  //   Sebastien Pouliot  <[email protected]>
009:  //
010:  // (C) Ximian, Inc.  http://www.ximian.com
011:  // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
012:  //
013:  // Permission is hereby granted, free of charge, to any person obtaining
014:  // a copy of this software and associated documentation files (the
015:  // "Software"), to deal in the Software without restriction, including
016:  // without limitation the rights to use, copy, modify, merge, publish,
017:  // distribute, sublicense, and/or sell copies of the Software, and to
018:  // permit persons to whom the Software is furnished to do so, subject to
019:  // the following conditions:
020:  //
021:  // The above copyright notice and this permission notice shall be
022:  // included in all copies or substantial portions of the Software.
023:  //
024:  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
025:  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
026:  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
027:  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
028:  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
029:  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
030:  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
031:  //
032:  
033:  
034:  // This is a wrapper around StreamReader used by System.Console that
035:  // catches IOException so that graphical applications don‘t suddenly
036:  // get IO errors when their terminal vanishes.  See
037:  // UnexceptionalStreamWriter too.
038:  
039:  using System.Text;
040:  using System.Runtime.InteropServices;
041:
042:  namespace System.IO {
043:      internal class UnexceptionalStreamReader : StreamReader {
044:
045:          private static bool[] newline = new bool [Environment.NewLine.Length];
046:
047:          private static char newlineChar;
048:
049:          static UnexceptionalStreamReader () {
050:              string n = Environment.NewLine;
051:              if (n.Length == 1)
052:                  newlineChar = n [0];
053:          }
054:
055:          public UnexceptionalStreamReader(Stream stream)
056:              : base (stream)
057:          {
058:          }
059:
060:          public UnexceptionalStreamReader(Stream stream, bool detect_encoding_from_bytemarks)
061:              : base (stream, detect_encoding_from_bytemarks)
062:          {
063:          }
064:
065:          public UnexceptionalStreamReader(Stream stream, Encoding encoding)
066:              : base (stream, encoding)
067:          {
068:          }
069:
070:          public UnexceptionalStreamReader(Stream stream, Encoding encoding, bool detect_encoding_from_bytemarks)
071:              : base (stream, encoding, detect_encoding_from_bytemarks)
072:          {
073:          }
074:
075:          public UnexceptionalStreamReader(Stream stream, Encoding encoding, bool detect_encoding_from_bytemarks, int buffer_size)
076:              : base (stream, encoding, detect_encoding_from_bytemarks, buffer_size)
077:          {
078:          }
079:
080:          public UnexceptionalStreamReader(string path)
081:              : base (path)
082:          {
083:          }
084:
085:          public UnexceptionalStreamReader(string path, bool detect_encoding_from_bytemarks)
086:              : base (path, detect_encoding_from_bytemarks)
087:          {
088:          }
089:
090:          public UnexceptionalStreamReader(string path, Encoding encoding)
091:              : base (path, encoding)
092:          {
093:          }
094:
095:          public UnexceptionalStreamReader(string path, Encoding encoding, bool detect_encoding_from_bytemarks)
096:              : base (path, encoding, detect_encoding_from_bytemarks)
097:          {
098:          }
099:
100:          public UnexceptionalStreamReader(string path, Encoding encoding, bool detect_encoding_from_bytemarks, int buffer_size)
101:              : base (path, encoding, detect_encoding_from_bytemarks, buffer_size)
102:          {
103:          }
104:
105:          public override int Peek ()
106:          {
107:              try {
108:                  return(base.Peek ());
109:              } catch (IOException) {
110:              }
111:
112:              return(-1);
113:          }
114:
115:          public override int Read ()
116:          {
117:              try {
118:                  return(base.Read ());
119:              } catch (IOException) {
120:              }
121:
122:              return(-1);
123:          }
124:
125:          public override int Read ([In, Out] char[] dest_buffer,
126:                        int index, int count)
127:          {
128:              if (dest_buffer == null)
129:                  throw new ArgumentNullException ("dest_buffer");
130:              if (index < 0)
131:                  throw new ArgumentOutOfRangeException ("index", "< 0");
132:              if (count < 0)
133:                  throw new ArgumentOutOfRangeException ("count", "< 0");
134:              // ordered to avoid possible integer overflow
135:              if (index > dest_buffer.Length - count)
136:                  throw new ArgumentException ("index + count > dest_buffer.Length");
137:
138:              int chars_read = 0;
139:              char nl = newlineChar;
140:              try {
141:                  while (count > 0) {
142:                      int c = base.Read ();
143:                      if (c < 0)
144:                          break;
145:                      chars_read++;
146:                      count--;
147:
148:                      dest_buffer [index] = (char) c;
149:                      // shortcut when a new line is only one character (e.g. Linux, Mac)
150:                      if (nl != (char)0) {
151:                          if ((char)c == nl)
152:                              return chars_read;
153:                      } else {
154:                          if (CheckEOL ((char)c))
155:                              return chars_read;
156:                      }
157:                      index ++;
158:                  }
159:              } catch (IOException) {
160:              }
161:
162:              return chars_read;
163:          }
164:
165:          private bool CheckEOL (char current)
166:          {
167:              // general case for any length (e.g. Windows)
168:              for (int i=0; i < newline.Length; i++) {
169:                  if (!newline [i]) {
170:                      if (current == Environment.NewLine [i]) {
171:                          newline [i] = true;
172:                          return (i == newline.Length - 1);
173:                      }
174:                      break;
175:                  }
176:              }
177:              for (int j=0; j < newline.Length; j++)
178:                  newline [j] = false;
179:              return false;
180:          }
181:
182:          public override string ReadLine()
183:          {
184:              try {
185:                  return(base.ReadLine ());
186:              } catch (IOException) {
187:              }
188:
189:              return(null);
190:          }
191:
192:          public override string ReadToEnd()
193:          {
194:              try {
195:                  return(base.ReadToEnd ());
196:              } catch (IOException) {
197:              }
198:
199:              return(null);
200:          }
201:      }
202:  }

上述源程序定义了 UnexceptionalStreamReader 类。该类位于 System.IO 命名空间中,继承自 StreamReader 类,是 internal 的,即只能在本程序集中使用。

UnexceptionalStreamReader 类是对 StreamReader 类的包装,捕获并忽略所有的 IOException 异常。它用于 Console 类中,以免 IO 错误干扰控制台的正常运行。

UnexceptionalStreamReader 类定义了以下两个私有静态字段:

  1. newline: private static,类型为 bool[]。(第 45 行)
  2. newlineChar: private static,类型为 char。(第 47 行)

第 49 行到第 53 行的静态构造函数对 newlineChar 字段有条件地赋值。

第 55 行到第 103 行的十个构造函数仅是调用基类 StreamReader 相应的构造函数而已。

第 105 行到第 123 行以及第 182 行到第 200 行的四个方法重写了基类 StreamReader 中相应的虚方法,简单地调用基类中相应的虚方法,捕获并忽略 IOException 异常。

第 125 行到第 163 行的 Read 方法也是重写了基类的虚方法,但是它没有简单地调用基类中相应的虚方法。它首先检查输入的参数是否合法,然后在循环中调用基类的另一个 Read 方法一个个地读取字符,捕获并忽略 IOException 异常。注意这个 Read 方法的语义不同于其基类 StreamReader 中相应的 Read 方法的语义,前者遇到 EOL 就提前返回,而后者要遇到 EOF
才会提前返回。

第 165 行到第 180 行的 CheckEOL 方法是私有(private)方法,仅在第 154 行被调用过,用于在 Environment.NewLine 不止一个字符时(在 Windows
操作系统中就是如此,其值为”\r\n”,而在 Unix 操作系统中其值为”\n”)判断是否遇到了行结束符,即 EOL,也就是 Environment.NewLine。

在 Console.dll 项目中,UnexceptionalStreamReader 类在 Console.cs 中被使用过一次。在上述源程序的第 34 行到第 37 行的注释中也已经指出了这一点。

UnexceptionalStreamWriter 类 (internal class)

下面就是 mcs/class/corlib/System.IO/UnexceptionalStreamWriter.cs:

001:  //
002:  // System.IO.StreamWriter.cs
003:  //
004:  // Authors:
005:  //   Dietmar Maurer ([email protected])
006:  //   Paolo Molaro ([email protected])
007:  //   Dick Porter ([email protected])
008:  //
009:  // (C) Ximian, Inc.  http://www.ximian.com
010:  //
011:  
012:  //
013:  // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
014:  //
015:  // Permission is hereby granted, free of charge, to any person obtaining
016:  // a copy of this software and associated documentation files (the
017:  // "Software"), to deal in the Software without restriction, including
018:  // without limitation the rights to use, copy, modify, merge, publish,
019:  // distribute, sublicense, and/or sell copies of the Software, and to
020:  // permit persons to whom the Software is furnished to do so, subject to
021:  // the following conditions:
022:  //
023:  // The above copyright notice and this permission notice shall be
024:  // included in all copies or substantial portions of the Software.
025:  //
026:  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
027:  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
028:  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
029:  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
030:  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
031:  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
032:  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
033:  //
034:  
035:  
036:  // This is a wrapper around StreamWriter used by System.Console that
037:  // catches IOException so that graphical applications don‘t suddenly
038:  // get IO errors when their terminal vanishes (ie when they spew debug
039:  // output.)  See UnexceptionalStreamReader too.
040:  
041:  using System.Text;
042:  using System;
043:
044:  namespace System.IO {
045:      internal class UnexceptionalStreamWriter: StreamWriter {
046:          public UnexceptionalStreamWriter (Stream stream)
047:              : base (stream)
048:          {
049:          }
050:
051:          public UnexceptionalStreamWriter (Stream stream,
052:                            Encoding encoding)
053:              : base (stream, encoding)
054:          {
055:          }
056:
057:          public UnexceptionalStreamWriter (Stream stream,
058:                            Encoding encoding,
059:                            int bufferSize)
060:              : base (stream, encoding, bufferSize)
061:          {
062:          }
063:
064:          public UnexceptionalStreamWriter (string path)
065:              : base (path)
066:          {
067:          }
068:
069:          public UnexceptionalStreamWriter (string path, bool append)
070:              : base (path, append)
071:          {
072:          }
073:
074:          public UnexceptionalStreamWriter (string path, bool append,
075:                            Encoding encoding)
076:              : base (path, append, encoding)
077:          {
078:          }
079:
080:          public UnexceptionalStreamWriter (string path, bool append,
081:                            Encoding encoding,
082:                            int bufferSize)
083:              : base (path, append, encoding, bufferSize)
084:          {
085:          }
086:
087:          public override void Flush ()
088:          {
089:              try {
090:                  base.Flush ();
091:              } catch (Exception) {
092:              }
093:          }
094:
095:          public override void Write (char[] buffer, int index,
096:                          int count)
097:          {
098:              try {
099:                  base.Write (buffer, index, count);
100:              } catch (IOException) {
101:              }
102:          }
103:
104:          public override void Write (char value)
105:          {
106:              try {
107:                  base.Write (value);
108:              } catch (IOException) {
109:              }
110:          }
111:
112:          public override void Write (char[] value)
113:          {
114:              try {
115:                  base.Write (value);
116:              } catch (IOException) {
117:              }
118:          }
119:
120:          public override void Write (string value)
121:          {
122:              try {
123:                  base.Write (value);
124:              } catch (IOException) {
125:              }
126:          }
127:      }
128:  }

上述源程序定义了 UnexceptionStreamWriter 类。该类位于 System.IO 命名空间中,继承自 StreamWriter 类,是 internal 的,即只能在本程序集中使用。

第 2 行的注释“System.IO.StreamWriter.cs”有误,应改为“System.IO.UnexceptionStreamWriter”。

UnexceptionalStreamWriter 类是对 StreamWriter 类的包装,捕获并忽略所有的 IOException 异常。它用于 Console 类中,以免 IO 错误干扰控制台的正常运行。

第 46 行到第 85 行的七个构造函数仅是调用基类 StreamWriter 相应的构造函数而已。

第 87 行到第 126 行的五个方法重写了基类 StreamWriter 中相应的虚方法,简单地调用基类中相应的虚方法,捕获并忽略 IOException 异常。

在 Console.dll 项目中,UnexceptionStreamWriter 类在 Console.cs 中被使用过两次。

版权声明:本文为博主http://www.zuiniusn.com原创文章,未经博主允许不得转载。

时间: 2024-11-10 13:06:47

Mono源代码学习笔记:Console类(四)的相关文章

Mono源代码学习笔记:Console类(一)

前言 我们知道,Mono 是 .NET Framework 跨平台的开源实现.Mono 的源代码就是金矿,等待我们去挖掘. 目前 Mono 的最新版本是 Mono 2.8.2,可以到 http://ftp.novell.com/pub/mono/sources/mono/ 下载 mono-2.8.2.tar.bz2,文件大小是30MB.可以参阅"在 Ubuntu 10.10 操作系统安装 Mono 2.8.2"一文. 现在,让我们来看看 Mono 是如何实现 .NET Framewor

Mono源代码学习笔记:Console类(六)

Unix 终端的基础知识 许多 Unix 系统使用终端.但是在今天的许多情况下,终端也许是一个运行终端程序的 PC 机.从历史上来说,不同的生产商提供了大量的硬件终端.Linux 操作系统包含一个环境变量 TERM,用来表示我们正在使用的终端的类型,如下所示: [email protected]:~$ w 16:35:13 up 6 days, 7:36, 2 users, load average: 0.62, 0.34, 0.25 USER TTY FROM [email protected

Mono源代码学习笔记:Console类(五)

CStreamReader 类 (internal class) 下面就是 mcs/class/corlib/System/CStreamReader.cs: 001: // 002: // System.CStreamReader 003: // 004: // Authors: 005: // Dietmar Maurer ([email protected]) 006: // Miguel de Icaza ([email protected]) 007: // Dick Porter (

Mono源代码学习笔记:Console类(三)

Buffer 类 (public static class) 下面就是 mcs/class/corlib/System/Buffer.cs: 001: // 002: // System.Buffer.cs 003: // 004: // Authors: 005: // Paolo Molaro ([email protected]) 006: // Dan Lewis ([email protected]) 007: // 008: // (C) 2001 Ximian, Inc. http

Mono源码学习笔记:Console类(四)

NullStream 类 (internal class) 以下就是 mcs/class/corlib/System.IO/NullStream.cs: 01: namespace System.IO 02: { 03: class NullStream : Stream 04: { 05: public override bool CanRead { get { return true; } } 06: public override bool CanSeek { get { return t

Mono源码学习笔记:Console类(三)

Buffer 类 (public static class) 以下就是 mcs/class/corlib/System/Buffer.cs: 001: // 002: // System.Buffer.cs 003: // 004: // Authors: 005: // Paolo Molaro ([email protected]) 006: // Dan Lewis ([email protected]) 007: // 008: // (C) 2001 Ximian, Inc. http

《Javascript权威指南》学习笔记之十四:JavaScript内建类

前面的几篇博文分别介绍了对象.字符串.数组.日期等内建类,本篇将介绍Boolean/Math/Function/Arguments类 一.使用Boolean类处理逻辑值 Boolean类是JS的一个封装类,可以用于获取Boolean对象的原始数据类型或者字符串表示形式.new Boolean(value)用于创建一个Boolean对象,Boolean(value)它的参数转换成一个原始的布尔值,并且返回这个值.Boolean对象只有两个值:true或者false. value参数可以省略.如果省

C++ Primer 学习笔记_23_类与数据抽象(9)--四种对象生存期和作用域、static 用法总结

C++ Primer 学习笔记_23_类与数据抽象(9)--四种对象生存期和作用域.static 用法总结 前言: 从上图可知,程序占用的内存被分了以下几部分. (1).栈区(stack) 存放函数的参数值,局部变量的值等,内存的分配是连续的.栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁,其特点是效率高,但空间大小有限 注意:通常栈空间容量比较小,一般是1MB-2MB,所以体积比较大的对象不适合在栈中分配. (2).堆区(heap) 由malloc系列函数或new操作符

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员)、拷贝构造函数

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员).拷贝构造函数  从概念上将,可以认为构造函数分为两个阶段执行: 1)初始化阶段: 2)普通的计算阶段.计算阶段由构造函数函数体中的所有语句组成. 一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 1.对象成员及其初始化 <span style="font-size:14px;">#include <iostream> using namespace std;