Fork me on GitHub

readonly, const, static, static readonly 关键字实例说明

      关于 readonly 和 const 的区别,网上已经有很多人撰文作了特别的说明。小G在这里也是参考了网上很多人的文章和MSDN的相关内容才写出的这篇随笔。关于这些修改符的理解,是我个人编程时的经验总结,如有不妥之处,欢迎大家拍砖。

     原本只是想说说 static 的使用场景,但后来觉得这些修饰关键字都相关,索性就都拿出来讲讲,权当积累了。这些关键字是C#语法中非常基础的部分,没错,非常基础。但是我发现在真实项目中,能将这些关键字应用到正确场景中的人并不多,所以我觉得有必要老调重弹,详细说说它们的用法。

    个人认为,其实 readonly 和 const 属于一类关键字,它们只用于修饰字段,决定字段何时被赋值;而 static 则不同,它可以修改字段和方法,决定类型成员是否从属于某一具体的实例,这个我们后面会用实例来作说明。

    1.readonly 关键字

    readonly 关键字是可以在字段上使用的修饰符。 当字段声明包括 readonly 修饰符时,该声明引入的字段赋值只能作为声明的一部分出现,或者出现在同一类的构造函数中。 这是MSDN上对readonly关键字的解释,是不是有点晦涩难懂?嗯,这是MSDN一贯的作风,哈哈。下面我们用实例来作演示,说明 readonly 具体的应用场景。

    “该声明引入的字段赋值只能作为声明的一部分出现”,这句话的意思是,readonly 修改的字段,可以在声明时赋值;“或者出现在同一类的构造函数中”,这句话的意思是,readonly 所修饰的字段,也可以在类的构造函数中赋值。我们以人(People)为例,用代码实例来作说明:

   1: void Main()
   2: {
   3:     Male m=new Male("男");
   4:     Female fm=new Female("女");
   5:     Console.WriteLine("男士性别:" + m.GetGender);
   6:     Console.WriteLine("女士性别:" + fm.GetGender);
   7: }
   8:  
   9: // 人的基类
  10: public class People
  11: {
  12:     private readonly string _gender="男";
  13:     public People(){}
  14:     public People(string gender)
  15:     {
  16:         this._gender=gender;
  17:     }
  18:     public string GetGender
  19:     {
  20:         get
  21:         {
  22:             return this._gender;
  23:         }
  24:     }    
  25: }
  26: // 男性
  27: public class Male:People
  28: {
  29:     public Male(string gender):base(gender)
  30:     {        
  31:     }
  32: }
  33: // 女性
  34: public class Female:People
  35: {
  36:     public Female(string gender):base(gender)
  37:     {        
  38:     }
  39: }

      上面这段代码很好理解,我创建了一个 People 的基类,基类中包含一个 readonly 字段 Gender,即性别,并赋予了默认值“男”(没有别的意思,因为大家通常都叫我们先生嘛(^O^));然后派生出了 Male (男性)和 Female (女性)两个具体的类型,并分别创建了它们的含参构造函数,当然,你可以为这两个派生类创建默认的无参构造,但我肯定你不愿意那样做,因为那样做这个世界就真的大一统了,清一色的光棍啊,吼吼~~~你肯定很难想象没有女孩子这个世界会变成什么样子,所以,老老实实地创建含参构造吧(^_^)。

     这个实例的结果如下:

      男士性别:男
       女士性别:女

     这看起来没什么特别的,下面我们在 People 基类里添加一个测试方法,试图修改 gender 的值,看看会发生什么:

image

     看到了没?编辑器直接报错了,在构造函数之外的其他方法中修改 readonly 字段是不允许的,这跟人一出生即确定性别是一个道理(当然人类已经逆天了,那部分我们暂且不予理会(-_-)!!!)。

     2.const 关键字

     const 关键字用于修改字段或局部变量的声明。 它指定字段或局部变量的值是常数,不能被修改。   相比之下,这个关键字就容易理解的多了,const 变量在我看来,它就是一个占位符,是C#的设计者为了让大家更好操作常量而允许大家临时定义的一个标记。如果大家用 ILspy 或 Reflector 反编译过.NET程序包,就会发现,const 字段在程序集中全部被替换成了真实的值而不再是变量名。

     仍然用实例来做说明,我们在 People 基类中添加下面这些属性:

   1: private const int _numberOfFeet = 2 ;
   2: // 脚的数量
   3:  public int NumberOfFeet
   4: {
   5:     get
   6:     {
   7:         return _numberOfFeet;
   8:     }
   9: }

      这个属性的作用也很简单,定义了一个常量,用以指示人类脚的数量,并公开一个 NumberOfFeet 属性允许派生类或引用类去获取这个值。我们来尝试给这个属性添加一个 set 写操作器给常量 _numberOfFeet 赋值,看看会发生什么:

image

      提示已经写得很清楚了,赋值号(即=)左侧必须是个变量、属性或索引器,这也说明 _numberOfFeet 是一个常量,上面这个 set 方法体的内容和 2 = 3 的效果是一样的,这显然不符合C#语法要求,而且也不符合逻辑。const 字段不光在实例方法中不可修改,在构造函数和外部引用中也不能对其进行赋值操作,即 const 字段一经声明,不可更改。这里要注意,const 本身即是包含 static 修饰的,所以 get 方法体内,我没有使用 this._numberOfFeet 来调用它。

      3.static 关键字

      使用 static 修饰符声明属于类型本身而不是属于特定对象的静态成员。 static 修饰符可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。 可以这么理解,static 用于指定哪些类型成员是公用的,包括字段、属性、方法、运算符、事件和构造函数;如果一个类被声明为 static,那么此类型不可被实例化,它的所有类型成员都是公用的,也即我们常说的工具类。例如我们经常使用的 Console 类和 Enumerable 类,下面是 Console 类和 Enumerable类 的类型声明:

image

image

我们来给 People 类的基类添加一个方法,用于统计人口总数;这个方法不应该从属于某个具体的 People 类实例,无论 Male 还是 Female,只要一出生(创建),世界人口就应该自动递增:

   1: void Main()
   2: {
   3:     Male m=new Male("男");
   4:     Console.WriteLine("当前人口总数:" + m.Population);
   5:     Female fm=new Female("女");
   6:     Console.WriteLine("当前人口总数:" + fm.Population);
   7:     // Console.WriteLine("男士性别:" + m.GetGender);
   8:     // Console.WriteLine("女士性别:" + fm.GetGender);
   9: }
  10:  
  11: // 人的基类
  12: public class People
  13: {
  14:     private readonly string _gender="男";
  15:     public People(){}
  16:     public People(string gender)
  17:     {
  18:         this._gender=gender;
  19:     }
  20:     public string GetGender
  21:     {
  22:         get
  23:         {
  24:             return this._gender;
  25:         }
  26:     }
  27:     private const int _numberOfFeet = 2 ;
  28:     // 脚的数量
  29:     public int NumberOfFeet
  30:     {
  31:         get
  32:         {
  33:             return _numberOfFeet;
  34:         }
  35:     }
  36:     
  37:     private static int _count = 0;
  38:     // 人口递增
  39:     public void AddCount()
  40:     {
  41:         _count++;
  42:     }
  43:     // 获取人口总数
  44:     public virtual int Population
  45:     {
  46:         get
  47:         {
  48:             return _count;
  49:         }
  50:     }
  51: }
  52: // 男性
  53: public class Male:People
  54: {
  55:     public Male(string gender):base(gender)
  56:     {        
  57:         base.AddCount();
  58:     }
  59: }
  60: // 女性
  61: public class Female:People
  62: {
  63:     public Female(string gender):base(gender)
  64:     {        
  65:         base.AddCount();
  66:     }
  67: }

这一次,我们在 People 基类中添加了静态字段_count,并添加 AddCount() 方法使其递增,随后修改了 Male 和 Female 的构造函数,调用基类的 AddCount 方法来增加人口数量,程序执行结果如下:

image

      我们看到只要新增一个 People 的实例,无论是 Male 还是 Female,人口总数都会递增。这其实就是 static 关键字的作用,它使得类型成员可以在多个实例间共用。BTW,来说说误用 static 变量的情形吧。

      我们知道 ASP.NET  应用中,IIS 本身就是使用多线程来响应用户的请求的。来设想这样一个场景,码农在一个页面后台中添加了一个全局的静态变量,好吧,如果他想在多个用户间传递数据,貌似这样也是可行的,只是所有人都只能拿到最后一次赋值的结果。但是,如果他把用户名或是其他验证信息声明为静态变量,而在权限验证之类的地方又刚好引用了这个变量,想想下面会发生什么。当系统管理员访问这个页面时,当前访问这个页面的用户都拥有管理员权限,直至下一用户登录;再极端一点,如果你把这个变量放在了 BasePage 里,那么将迎来另一次大一统,访问派生自 BasePage 页面的用户,都将拥有同最后登录用户的权限验证信息。好了,乡亲们,无论如何,把私密用户信息共享真的是一个很不负责的做法。

      你可能会说我不会犯这样的错误,但这并不代表其他人不会。没错,这是最基本的C#语法,但是我想说,如果我们写了好几年的.NET程序,至今还没搞清楚这些基本概念,抑或是你到现在还不清楚 string 和 StringBuilder 的应用场景一样,这绝对是一件很悲催的事情。

      4. static readonly 关键字

      这实际上并不是一个独立的关键字了,这是前面两个关键字的组合。我们照猫画虎,来定义一下这个关键字的应用场景。readonly 只能用于字段修饰,static 决定字段属于类型而不是特定的对象实例,于是我们可以得出以下定义:

      static readonly 修饰的字段,只能在声明时赋值,或是在静态构造中赋值(关于静态构造的注意事项请参见:《关于C#静态构造函数的几点说明》)。

      static readonly 与 const 的应用场景已十分相似,区别在于,static readonly 修饰的是运行时常量,而 const 修饰的是编译时常量;static readonly 可以用于修饰引用型变量,而 const 修饰的引用型变量只能是 string 或 null(其他引用型变量的构造函数初始化是在运行时,而非编译时)。

     下面是微软的 Color 类的构造示例,演示了 static readonly 的具体应用场景:

   1: public class Color
   2: {
   3:     public static readonly Color Black = new Color(0, 0, 0);
   4:     public static readonly Color White = new Color(255, 255, 255);
   5:     public static readonly Color Red = new Color(255, 0, 0);
   6:     public static readonly Color Green = new Color(0, 255, 0);
   7:     public static readonly Color Blue = new Color(0, 0, 255);
   8:     private byte red, green, blue;
   9:  
  10:     public Color(byte r, byte g, byte b)
  11:     {
  12:         red = r;
  13:         green = g;
  14:         blue = b;
  15:     }
  16: }

     这是特别说明一下,string 是一个非常特殊的引用型类型,详细的解释,请参见:《C# string 到底是引用类型还是值类型》。

posted @ 2012-11-28 15:24  TimGong  阅读(832)  评论(1编辑  收藏  举报