C#箴言:定义常量的两种方法

[ 2345 查看 / 12 回复 ]

在C#中定义常量的方式有两种,一种叫做静态常量(Compile-time constant),另一种叫做动态常量(Runtime constant)。前者用“const”来定义,后者用“readonly”来定义。

   对于静态常量(Compile-time constant),它的书写方式如下:

   public const int MAX_VALUE = 10;

   为什么称它为静态常量呢,因为如上声明可以按照如下理解(注意:如下书写是错误的,会出编译错误,这里只是为了方便说明)。

   public static const int MAX_VALUE = 10;

   用const定义的常量,对于所有类对象而言都是一样的,因此需要像访问静态成员那样去访问const定义的常量,而用对象的成员方式去访问会出变异错误。此外,对于静态常量的访问在编译的时候,是用常量的值去替换常量,例如:

   int nValue = MAX_VALUE;

   这句在编译之后,和如下这句所产生的中间语言代码是一样的。

   int nValue = 10;

   不过,在用const来定义常量的时候,在类型上有很多限制。首先,此类型必须属于值类型,同时此类型的初始化不能通过new来完成,因此一些用struct定义的值类型常量也不能用const来定义。

   相对于const而言,用readonly来定义常量要灵活的多,它的书写方式如下:

   public readonly int MAX_VALUE = 10;

   为什么称为动态变量,因为系统要为readonly所定义的常量分配空间,即和类的其他成员一样拥有独立的空间。此外,readonly所定义的常量除了在定义的时候可以设定常量值外,还可以在类的构造函数中进行设定。由于readonly所定义的常量相当于类的成员,因此使用const来定义常量所受到的类型限制,在使用readonly去定义的时候全部消失,即可以用readonly去定义任何类型的常量。

   综合上面所述,至于对比两者之间的区别具体如下。

静态常量(Compile-time constant) 动态常量(Runtime constant)
定义 声明的同时要设置常量值。 声明的时候可以不需要进行设置常量值,可以在类的构造函数中进行设置。
类型限制 首先类型必须属于值类型范围,且其值不能通过new来进行设置。 没有限制,可以用它定义任何类型的常量。
对于类对象而言 对于所有类的对象而言,常量的值是一样的。 对于类的不同对象而言,常量的值可以是不一样的。
内存消耗 无。 要分配内存,保存常量实体。
综述 性能要略高,无内存开销,但是限制颇多,不灵活。 灵活,方便,但是性能略低,且有内存开销。

   对于在定义常量的时候,到底是用const来定义还是readonly来定义,我以前为了追求性能,因此尽量用const来定义。但是在此书中,提到了一个关于使用const会产生潜在的bug。就是在程序中使用DLL类库某个类的静态常量时,如果在类库中修改静态常量的值,其它接口没有发生变化,一般来说,程序调用端是不需要重新编译,直接执行就可以调用新的类库。不过就是在此情况下,会产生潜在的bug。这是由于静态常量在编译的时候,是用它的值去替换常量,因此在调用端的程序也是这样进行替换的。

   例如:在类库中定义了一个静态常量,如下:

public const int MAX_VALUE = 10;

   那么对于程序中调用此静态常量这段代码,在编译后产生的中间语言代码中,是用10来进行替换,即使用静态常量的地方,改为10了。

   那么当类库的静态变量发生变化后,例如:

public const int MAX_VALUE = 15;

   那么对于调用端程序是可以在没有重新编译的情况下进行运行,不过此时程序的中间语言代码对应于静态变量的值是10,而不是新类库中的15。因此这样产生的不一致,程序会引发潜在的bug。解决此类问题的方法,就是调用端程序在更新类库之后重新编译一下,即生成新的中间语言代码。

   对于如上在const定义常量时所存在的潜在bug,在用readonly定义常量时是不会发生的。因为readonly定义的常量类似于类的成员,因此在访问的时候需要根据具体常量地址来访问,从而避免此类bug。

   鉴于此,建议用readonly来替换const去定义常量。
2

评分次数

    本主题由 管理员 hhjjj444 于 2010-2-1 11:27:29 执行 设置精华/取消 操作
    TOP

    这还是一个 早期绑定 还是 晚期绑定 的问题。如果是在 编译期 就能确定的东西,就是 早绑定 ,亦即编译期常量;如果必须在 运行时 才进行绑定的,就是 晚绑定 。

    早绑定,就失去了很多特性。比如,多态(这里特指,override;编译期的overload多态,不在讨论之列),比如反射。上述两个特性,都是建立在 晚绑定 的基础之上的。

    new出来的东西,全都是 晚绑定 的 。晚绑定对于内存的开销更大。。对于楼主最后一句的建议,其实很简单,就看 需求 和 取舍。如果可以对 性能 要求不敏感,那么当然建议使用 晚绑定 ,可以得到很多面向对象的特性。
    TOP

    大概是看明白了,不知道用的时候会不会糊涂
    TOP

    原帖由 wanderer_hh 于 2010-2-1 10:00:00 发表
    大概是看明白了,不知道用的时候会不会糊涂


    早绑定和晚绑定,是面向对象体系中,至为重要的思想,值得仔细品味把握。
    TOP

    在C#中定义常量的方式有两种,一种叫做静态常量(Compile-time constant),另一种叫做动态常量(Runtime constant)。

    传说中的‘编译期常量’和‘运行时常量’的英语。。
    TOP

    说实话,如果翻译到中文,再加上望文生义的话,往往会产生误解。比如const和readonly就是典型一例。。readonly真实的含义完全是在运行时(Runtime,CLR管辖)行为。既然是在运行时的晚绑定行为,跟我们通常理解的的常量就完完全全不是一个概念了。。如果非要翻译成中文在进行解释,肯定会发生‘理解时’异常。

    readonly修饰的变量是在运行时赋值的。因此,readonly不能修饰 局部变量。局部变量的赋值是在编译时期完成的。因此,当一个局部变量在声明后不给赋值,编译器会报错(正是因为编译器负责在编译时给赋值)。当然,如果听了张老师关于out参数传引用的精彩讲解,又会发现一个例外。由于out传参,不需要在调用‘被调’函数之前赋值(也就是说,实参是不需要被赋值的),因此形如下面的代码,又刚好能通过编译。。编译器还是非常强大的。。

    int a ;//有下面这行代码,编译器不报错;没有下面的代码,编译器就报错。
    int b = ParseOut(out a);
    TOP

    嗯,很早的一个帖子,正好昨天我们上课讲到它了,再来讨论,又会加深理解.
    TOP

    新瓶虽装老酒,但这酒是 越陈越香 的啊。。呵呵!
    TOP

    const关键字,其实仅仅是给 C#编译器看的。。在CLR看来,它根本就跟一个 3.1415926 一样,就是一个常量。C#遇到这个关键字,就将其替换为 常量值,因此在经过 C# Compiler之后,Msil代码中,就是‘一堆数据’。。像 const double PI = 3.14d 这样的话,在编译之后,甚至连 PI这个变量名 都没有保留至 il代码中。

    这样,我们来理解 const 与 readonly的差异,就变得简单。 readonly 其实是给变量加以修饰,就是让某个变量仅仅可以在 实例/静态 构造器中被赋值(不够准确,还有种可能就是 内联初始化)。因此,过程块级的变量(局部变量)不可以使用 readonly修饰;

    反过来,被const 修饰的变量,它必须是 值类型(编译时绑定——运行时根本不可以是变量,而是常量)。当然,唯一的例外就是那个 String类型。其实我们看到,在所有的 引用类型 中,几乎所有的分配内存空间的方式都要用到 new,而String类型在初始化的时候,形如String str = "hello"; 没有用new,形态宛如 int i= 1(String这种类型——静态,主观,编译时 是引用类型,而 赋值动作真正产生的时候——动态、客观、运行时 又有 值类型 的表现)。所以,唯一可以被 const 修饰的引用类型就是 两栖类型 String。

    如果非要让const来修饰 引用类型,那只能赋值 null (且永远不能改变)。。在正常的程序中,几乎不可能有这种情况的出现——除非想到一种情况,就是给 null起个别名,在用到形如 if aaa== null 的时候,使用别名来代替 null(这样是不是有点无聊?)。仅仅是在 源代码 级别的一种 文字游戏而已。
    TOP

    来看一个例子加深理解。
    1.     class Test
          {
              readonly int pi = 3;
              public Test()
              {
                  pi = 4;
              }
          }
    复制代码
    TOP