技术开发 频道

详谈C# 语言中的 static 关键字

    【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));
  }

 

  注意上述源程序第 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;
      }
    }
}

 

  上述程序中第 44 行使用固定的种子初始化 Random 类的新实例,从而产生相同的随机数序列,以便用相同的测试用例来进行测试,使测试结果具有可比性。注意,在上述程序中如果 count 值和随机数的种子选取得不好,执行到第 33 行的 ZipInteger.Read 方法时是有可能抛出 EndOfStreamException 异常的。

0
相关文章