const和static readonly的区别

const
用于修改字段或局部变量的声明。它指定字段或局部变量的值是常数,不能被修改。常量的值必须在编译的时候确定,编译后,CLR将常量的值保存在Assembly的怨数据中。如果变量是const,那么他隐式的是static的。因此在声明常数的时候只需将该变量声明为const即可,而不允许在声明常数的时候使用static。
当代码引用常量时,CLR在元数据中查找该符号,将提取的常量值嵌入到IL中,所以常量没有地址以及相应的分配内存,而且不能通过引用传递变量

readonly
在字段上使用的修饰符,表示该字段是只读的。当一个字段在声明为readonly的时候,只有两种方式可以对其赋值,即作为声明的一部分出现,或者在同一类的构造函数中。
对于实例字段,在包含字段声明的类的实例构造函数中;或者,对于静态字段,在包含字段声明的类的静态构造函数中。也只有在这些上下文中,将 readonly 字段作为 out 或 ref 参数传递才有效。

static
声明属于类型本身而不是属于特定对象的静态成员,可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。
尽管类的实例包含该类所有实例字段的单独副本,但每个静态字段只有一个副本
不能通过类的实例引用静态成员,只可以通过类型名称引用它
如果对类应用 static 关键字,则该类的所有成员都必须是静态的
类(包括静态类)可以有静态构造函数。在程序开始和实例化类之间的某个时刻调用静态构造函数

const和readonly
const
1. 在编译期间解析的常量
2. 必须在声明就初始化
3. 既可用来修饰类中的成员,也可修饰函数体内的局部变量。
readonly
1. 在运行期间解析的常量,
2. 既可以在声明时初始化也可以在构造器中初始化,因此根据使用的构造函数,readonly的字段可能具有不同的值。
3. 只可以用于修饰类中的成员

const和static readonly
都表示静态的常量,赋值以后都不可以更改


我们都知道,const和static readonly的确很像:通过类名而不是对象名进行访问,在程序中只读等等。在多数情况下可以混用。
二者本质的区别在于,const的值是在编译期间确定的,因此只能在声明时通过常量表达式指定其值。而static readonly是在运行时计算出其值的,所以还可以通过静态构造函数来赋值。
明白了这个本质区别,我们就不难看出下面的语句中static readonly和const能否互换了:
1. static readonly MyClass myins = new MyClass();
2. static readonly MyClass myins = null;
3. static readonly A = B * 20;
   static readonly B = 10;
4. static readonly int [] constIntArray = new int[] {1, 2, 3};
5. void SomeFunction()
    {
      const int a = 10;
       ...
    }

1:不可以换成const。new操作符是需要执行构造函数的,所以无法在编译期间确定
2:可以换成const。我们也看到,Reference类型的常量(除了String)只能是Null。
3:可以换成const。我们可以在编译期间很明确的说,A等于200。
4:不可以换成const。道理和1是一样的,虽然看起来1,2,3的数组的确就是一个常量。
5:不可以换成readonly,readonly只能用来修饰类的field,不能修饰局部变量,也不能修饰property等其他类成员。

因此,对于那些本质上应该是常量,但是却无法使用const来声明的地方,可以使用static readonly。例如C#规范中给出的例子:

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);
static readonly需要注意的一个问题是,对于一个static readonly的Reference类型,只是被限定不能进行赋值(写)操作而已。而对其成员的读写仍然是不受限制的。

public static readonly MyClass myins = new MyClass();

myins.SomeProperty = 10;  //正常
myins = new MyClass();    //出错,该对象是只读的

但是,如果上例中的MyClass不是一个class而是一个struct,那么后面的两个语句就都会出错。

 

    private byte red, green, blue;

     public Color( byte r, byte g, byte b)
     {
         red = r;
         green = g;
         blue = b;
     }
}



C#拥有两种不同的常量:静态常量(compile-time constants)和动态常量(runtime constants)。它们有不同的特性,错误的使用不仅会损失效率,还可能造成错误。相比之下,静态常量在速度上会稍稍快一些,但是灵活性却比动态常量差很多。

//静态常量(隐式是静态的)
public const int compiletimeConstant = 1;
//动态常量
public static readonly runtimeConstant = 1;

      静态常量在编译时会将其替换为所对应的值,也就是说下面这2句话通过编译器编译后产生的IL是一样的。

//通过编译后二者会被翻译成相同的中间语言
int myNum = compiletimeConstant;
int myNum = 1;

      动态常量的值是在运行时获得的。IL中将其标为只读常量,而不是用常量的值代替。

      静态常量只能被声明为简单的数据类型(内建的int和浮点型)、枚举或字符串。下面的程序段是通不过编译的。你不能用new关键字初始化一个静态常量,即便是对一个值类型来说。

//这样是错误的
public const DateTime myDateTime = new DateTime(2006,9,1,0,0,0);
//这样是可以的
public static readonly DateTime myDateTime = new DateTime(2006,9,1,0,0,0);

       只读数据也是常量的一种,它们不能在构造器初始化之后被修改。但是它同静态常量不同,它的值是在运行时才被指派的,因此就会获得更大的灵活性。动态常量可以是任意的数据类型。

       二者最大的差别在于:静态常量在编译时会将其换为对应的值,这就意味着对于不同的程序集来说,当你改变静态常量的时候需要将其重新编译,否则常量的值不会发生变化,可能引发潜在的问题,而动态常量就不会有这种情况。

     用const定义的常量(隐式是静态的),需要像访问静态成员那样去访问const定义的常量,而用对象的成员方式去访问会出编译错误。 声明的同时要设置常量值。
      从另一方面来说,如果你的确要声明一些从不改变且处处唯一的常量,例如钩子函数SetWindowsHookEx的idHook参数或序列化时的版本等,就应该使用静态常量。但是用到这样的常量的机会不多。一般来说我们应该使用灵活性更高的动态常量。

               静态常量            动态常量

内存消耗        无                   因为要保存常量 有消耗

初始化          很少的简单类型,     任意类型,可以在类构造函数中赋值
                不能new,必须在
                声明同时赋值

何时发挥作用   编译时进行替换       相当于类中的数据成员

你可能感兴趣的:(readOnly)