【IT168 技术文档】ZipInteger 结构中有以下构造函数:
/// <summary>
/// 使用字节数组中的值初始 ZipInteger 结构的新实例
/// 注意:本构造函数会破坏传入的 bits 参数的值。
/// </summary>
/// <param name="bits">顺序为 big-endian 的字节值的数组</param>
public ZipInteger(byte[] bits)
{
if (bits == null) throw new ArgumentNullException("bits");
if (bits.Length < 1 || bits.Length > 9) throw new ArgumentException("Invalid length", "bits");
byte[] mask = { 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00 };
if (bits.Length > 1 && bits.Length < 9) bits[0] &= mask[bits.Length - 1];
Array.Reverse(bits);
Array.Resize(ref bits, 8);
if (!BitConverter.IsLittleEndian) Array.Reverse(bits);
data = Decode(BitConverter.ToInt64(bits, 0));
}
/// 使用字节数组中的值初始 ZipInteger 结构的新实例
/// 注意:本构造函数会破坏传入的 bits 参数的值。
/// </summary>
/// <param name="bits">顺序为 big-endian 的字节值的数组</param>
public ZipInteger(byte[] bits)
{
if (bits == null) throw new ArgumentNullException("bits");
if (bits.Length < 1 || bits.Length > 9) throw new ArgumentException("Invalid length", "bits");
byte[] mask = { 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00 };
if (bits.Length > 1 && bits.Length < 9) bits[0] &= mask[bits.Length - 1];
Array.Reverse(bits);
Array.Resize(ref bits, 8);
if (!BitConverter.IsLittleEndian) Array.Reverse(bits);
data = Decode(BitConverter.ToInt64(bits, 0));
}
注意上述源程序第 10 行的 mask 数组其实只需要初始化一次就行了,而不需要每次调用该构造函数时都进行初始化。也就是说,应该将 mask 变量声明为 static 的,如下所示:
static readonly byte[] mask = { 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00 };
愿望是美好的,现实是残酷的,加上 static 关键字后,再编译时 C# 编译器会报告以下错误:
error CS0106: 修饰符“static”对该项无效
也就是说,C# 语言不允许使用 static 修饰符来声明方法内部的变量。但是在 C/C++ 语言中是允许这么做的。
如果把该构造函数内部的 mask 变量提升到构造函数外部,成为 ZipInteger 结构的字段成员,就可以声明为 static 的。但是这样一样,读这段代码的人就不容易弄清楚 mask 字段只在这个构造函数内部使用,成为代码的“坏味道”,一点也不优雅了。
好了,让我们写一小段程序来测试一下加上 static 后对运行效率的影响:
using System;
using System.IO;
using System.Diagnostics;
using Skyiv.Numerics;
namespace Skyiv.Tester
{
sealed class StaticTester
{
static void Main()
{
try
{
new StaticTester().Run(100000000);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
void Run(int count)
{
Console.WriteLine(" OS Version: " + Environment.OSVersion);
Console.WriteLine(" CLR Version: " + Environment.Version);
using (var reader = new MemoryStream(GetBytes(count)))
{
var watch = Stopwatch.StartNew();
var n = 0;
for (var i = 0; i < 10; i++)
{
reader.Seek(0, SeekOrigin.Begin);
while (ZipInteger.Read(reader).HasValue) n++;
}
watch.Stop();
Console.WriteLine(" Count: " + n.ToString("N0"));
Console.WriteLine("Milliseconds: " + watch.ElapsedMilliseconds.ToString("N0"));
}
}
byte[] GetBytes(int count)
{
var bits = new byte[count];
var rand = new Random(123456);
rand.NextBytes(bits);
return bits;
}
}
}
using System.IO;
using System.Diagnostics;
using Skyiv.Numerics;
namespace Skyiv.Tester
{
sealed class StaticTester
{
static void Main()
{
try
{
new StaticTester().Run(100000000);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
void Run(int count)
{
Console.WriteLine(" OS Version: " + Environment.OSVersion);
Console.WriteLine(" CLR Version: " + Environment.Version);
using (var reader = new MemoryStream(GetBytes(count)))
{
var watch = Stopwatch.StartNew();
var n = 0;
for (var i = 0; i < 10; i++)
{
reader.Seek(0, SeekOrigin.Begin);
while (ZipInteger.Read(reader).HasValue) n++;
}
watch.Stop();
Console.WriteLine(" Count: " + n.ToString("N0"));
Console.WriteLine("Milliseconds: " + watch.ElapsedMilliseconds.ToString("N0"));
}
}
byte[] GetBytes(int count)
{
var bits = new byte[count];
var rand = new Random(123456);
rand.NextBytes(bits);
return bits;
}
}
}
上述程序中第 44 行使用固定的种子初始化 Random 类的新实例,从而产生相同的随机数序列,以便用相同的测试用例来进行测试,使测试结果具有可比性。注意,在上述程序中如果 count 值和随机数的种子选取得不好,执行到第 33 行的 ZipInteger.Read 方法时是有可能抛出 EndOfStreamException 异常的。