readonly vs. const [C#]
Feature:
readonly和const都是用来标识常量的。
const可用于修饰class的field或者一个局部变量(local variable);而readonly仅仅用于修饰class的field。
const常量的值必定在编译时就已明确并且恒定的;而readonly常量却有一点不同,那就是其值可以在运行时编译,当然,它也必须遵守作为常量的约束,那就是值必须恒定不变。
const常量必须在声明的同时对其进行赋值,并且确保该值在编译时可确定并恒定;而readonly常量则可以根据情况选择在声明的同时对其赋予一个编译时确定并恒定的值,或者将其值的初始化工作交给实例构造函数(instant constructor)完成。如:public readonly string m_Now = DateTime.Now.ToString();,m_Now会随着运行时实际情况变化而变化。
const常量属于类级别(class level)而不是实例对象级别(instant object level),所以它不能跟static结合一起使用,该常量的值将有整个类的所有实例对象共同分享。
readonly常量既可以使类级别也可以使实例对象级别的,这取决于它的声明以及初始化工作怎么实施。readonly可以与static结合使用,用于指定该常量属于类级别,并且把初始化工作交由静态构造函数(static constructor)完成。
若readonly常量的初始化工作在声明时就实施而不是交由实例构造函数完成的,并且没有与static结合使用,那么该常量对外行为也会表现得跟类级别常量一样。
能被const修饰声明为常量的类型必须是以下的基元类型(primitive type):sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, float, bool, decimal, string。
object, 数组(Array)和结构(struct)不能被声明为const常量。
Example:
using System;public class Order{ public Order(string id) { m_ID = id; } // 对于每一份订单,其订单序号都是实时确定的常量。 private readonly string m_ID; public string ID { get { return m_ID; } } public override string ToString() { return "Order ID: " + m_ID; }}using System;class Customer{ public Customer(string name, int kind) { m_Name = name; m_Kind = kind; } public const int NORMAL = 0; public const int VIP = 1; public const int SUPER_VIP = 2; private string m_Name; public string Name { get { return m_Name; } } private readonly int m_Kind; public int Kind { get { return m_Kind; } } public override string ToString() { if(m_Kind == SUPER_VIP) return "Name: " + m_Name + "[SuperVip]"; else if(m_Kind == VIP) return "Name: " + m_Name + "[Vip]"; else return "Name: " + m_Name + "[Normal]"; }}Remark:
一般情况下,如果你需要声明的常量是普遍公认的并作为单个使用,例如圆周率,黄金分割比例等。你可以考虑使用const常量,如:public const double PI = 3.1415926;。如果你需要声明常量,不过这个常量会随着实际的运行情况而决定,那么,readonly常量将会是一个不错的选择,例如上面第一个例子的订单号Order.ID。
一般情况下,如果你需要表达一组相关的编译时确定常量,你可以考虑使用枚举类型(enum),而不是把多个const常量直接嵌入到class中作为field。
using System;enum CustomerKind{ SuperVip, Vip, Normal}class Customer{ public Customer(string name, CustomerKind kind) { m_Name = name; m_Kind = kind; } private string m_Name; public string Name { get { return m_Name; } } private CustomerKind m_Kind; public CustomerKind Kind { get { return m_Kind; } } public override string ToString() { return "Name: " + m_Name + "[" + m_Kind.ToString() + "]"; }}然而,当这种结合使用枚举和条件判断的代码阻碍了你进行更灵活的扩展,并有可能导致日后的维护成本增加,你可以代之以多态,使用Replace Conditional with Polymorphism来对代码进行重构。