Features:

  • readonly和const都是用来标识常量的[1]。
  • const可用于修饰class的field或者一个局部变量(localvariable);而readonly仅仅用于修饰class的field。
  • const常量的值必定在编译时就已明确并且恒定的;而readonly常量却有一点不同,那就是其值可以在运行时编译,当然,它也必须遵守作为常量的约束,那就是值必须恒定不变。
  • const常量必须在声明的同时对其进行赋值,并且确保该值在编译时可确定并恒定;而readonly常量则可以根据情况选择在声明的同时对其赋予一个编译时确定并恒定的值,或者将其值的初始化工作交给实例构造函数(instant constructor)完成。如:public readonly string m_Now =DateTime.Now.ToString();,m_Now会随着运行时实际情况变化而变化。
  • const常量属于类级别(class level)而不是实例对象级别(instant objectlevel),并且它不能跟static结合一起使用,该常量的值将由整个类的所有实例对象共同分享(详细论述参见后面的Remark区域)。
  • readonly常量既可以是类级别也可以是实例对象级别的,这取决于它的声明以及初始化工作怎么实施。readonly可以与static结合使用,用于指定该常量属于 类级别,并且把初始化工作交由静态构造函数(static constructor)完成(有关如何把readonly常量声明为类级别或实例对象级别的论述清参见后面的Remark区域) 。
  • 能 被const修饰声明为常量的类型必须是以下的基元类型(primitive type):sbyte, byte,short, ushort, int, uint, long, ulong, char, float, double, float, bool, decimal, string。
  • object, 数组(Array)和结构(struct)不能被声明为const常量。
  • 一般情况下,引用类型是不能被声明为const常量的,不过有一个例外:string。该引用类型const常量的值可以有两种情况,string或 null。其实,string虽然是引用类型,但是.NET却对它特别处理,这种处理叫做字符串恒定性(immutable),使得string的值具有 只读特性。有关字符串恒定性的内容,可以参考​ ​​ ​​。

Examples:

Effective C++ (2) C#中的Const和Readonly_引用类型using System;

Effective C++ (2) C#中的Const和Readonly_引用类型_02

Effective C++ (2) C#中的Const和Readonly_数据库_03public class Order

Effective C++ (2) C#中的Const和Readonly_数据库_04Effective C++ (2) C#中的Const和Readonly_数据库_05Effective C++ (2) C#中的Const和Readonly_初始化_06{

Effective C++ (2) C#中的Const和Readonly_初始化_07 public Order()

Effective C++ (2) C#中的Const和Readonly_构造函数_08Effective C++ (2) C#中的Const和Readonly_数据库_09Effective C++ (2) C#中的Const和Readonly_数据库_10{

Effective C++ (2) C#中的Const和Readonly_数据库_11       Guid guid = Guid.NewGuid();

Effective C++ (2) C#中的Const和Readonly_数据库_12       ID = guid.ToString("D");

Effective C++ (2) C#中的Const和Readonly_构造函数_13   }

Effective C++ (2) C#中的Const和Readonly_数据库_14

Effective C++ (2) C#中的Const和Readonly_构造函数_15 // 对于每一份订单,其订单序号都是实时确定的常量。

Effective C++ (2) C#中的Const和Readonly_引用类型_16 public readonly string ID;

Effective C++ (2) C#中的Const和Readonly_初始化_17

Effective C++ (2) C#中的Const和Readonly_数据库_18 public override string ToString()

Effective C++ (2) C#中的Const和Readonly_引用类型_19Effective C++ (2) C#中的Const和Readonly_初始化_20Effective C++ (2) C#中的Const和Readonly_c++_21{

Effective C++ (2) C#中的Const和Readonly_构造函数_22 return "Order ID: " + ID;

Effective C++ (2) C#中的Const和Readonly_构造函数_23   }

Effective C++ (2) C#中的Const和Readonly_构造函数_24}

Explaintion:

  • 如果结合数据库使用,ID field通常都会都会与某个表的主健(primarykey)关联起来,如Orders表的OrderID。
  • 数据库的主健通常采用以下三种方式:
  • 自动递增值。你可以通过把DataColumn.AutoIncrement设定为true值来激活自动递增特性。
  • 唯一名称。这个是使用自己定义的算法来生成一个唯一序列号。
  • GUID(全局唯一标识符)。你可以通过System.Guid结构来生成GUID,如上例。

Effective C++ (2) C#中的Const和Readonly_引用类型_25using System;

Effective C++ (2) C#中的Const和Readonly_初始化_26

Effective C++ (2) C#中的Const和Readonly_c++_27class Customer

Effective C++ (2) C#中的Const和Readonly_构造函数_28Effective C++ (2) C#中的Const和Readonly_构造函数_29Effective C++ (2) C#中的Const和Readonly_初始化_30{

Effective C++ (2) C#中的Const和Readonly_初始化_31 public Customer(string name, int kind)

Effective C++ (2) C#中的Const和Readonly_构造函数_32Effective C++ (2) C#中的Const和Readonly_初始化_33Effective C++ (2) C#中的Const和Readonly_构造函数_34{

Effective C++ (2) C#中的Const和Readonly_引用类型_35       m_Name = name;

Effective C++ (2) C#中的Const和Readonly_构造函数_36       m_Kind = kind;

Effective C++ (2) C#中的Const和Readonly_构造函数_37   }

Effective C++ (2) C#中的Const和Readonly_初始化_38

Effective C++ (2) C#中的Const和Readonly_引用类型_39 public const int NORMAL = 0;

Effective C++ (2) C#中的Const和Readonly_初始化_40 public const int VIP = 1;

Effective C++ (2) C#中的Const和Readonly_构造函数_41 public const int SUPER_VIP = 2;

Effective C++ (2) C#中的Const和Readonly_c++_42

Effective C++ (2) C#中的Const和Readonly_c++_43 private string m_Name;

Effective C++ (2) C#中的Const和Readonly_引用类型_44 public string Name

Effective C++ (2) C#中的Const和Readonly_数据库_45Effective C++ (2) C#中的Const和Readonly_数据库_46Effective C++ (2) C#中的Const和Readonly_c++_47{

Effective C++ (2) C#中的Const和Readonly_c++_48Effective C++ (2) C#中的Const和Readonly_数据库_49 get Effective C++ (2) C#中的Const和Readonly_c++_50{ return m_Name; }

Effective C++ (2) C#中的Const和Readonly_构造函数_51   }

Effective C++ (2) C#中的Const和Readonly_引用类型_52

Effective C++ (2) C#中的Const和Readonly_数据库_53 private readonly int m_Kind;

Effective C++ (2) C#中的Const和Readonly_数据库_54 public int Kind

Effective C++ (2) C#中的Const和Readonly_数据库_55Effective C++ (2) C#中的Const和Readonly_c++_56Effective C++ (2) C#中的Const和Readonly_构造函数_57{

Effective C++ (2) C#中的Const和Readonly_初始化_58Effective C++ (2) C#中的Const和Readonly_引用类型_59 get Effective C++ (2) C#中的Const和Readonly_c++_60{ return m_Kind; }

Effective C++ (2) C#中的Const和Readonly_初始化_61   }

Effective C++ (2) C#中的Const和Readonly_c++_62

Effective C++ (2) C#中的Const和Readonly_数据库_63 public override string ToString()

Effective C++ (2) C#中的Const和Readonly_引用类型_64Effective C++ (2) C#中的Const和Readonly_数据库_65Effective C++ (2) C#中的Const和Readonly_构造函数_66{

Effective C++ (2) C#中的Const和Readonly_构造函数_67 if(m_Kind == SUPER_VIP)

Effective C++ (2) C#中的Const和Readonly_数据库_68 return "Name: " + m_Name + "[SuperVip]";

Effective C++ (2) C#中的Const和Readonly_构造函数_69 else if(m_Kind == VIP)

Effective C++ (2) C#中的Const和Readonly_引用类型_70 return "Name: " + m_Name + "[Vip]";

Effective C++ (2) C#中的Const和Readonly_初始化_71 else

Effective C++ (2) C#中的Const和Readonly_数据库_72 return "Name: " + m_Name + "[Normal]";

Effective C++ (2) C#中的Const和Readonly_初始化_73   }

Effective C++ (2) C#中的Const和Readonly_数据库_74}

Remarks:

  • 一般情况下,如果你需要声明的常量是普遍公认的并作为单个使用,例如圆周率,黄金分割比例等。你可以考虑使用const常量,如:publicconst double PI =3.1415926;。如果你需要声明常量,不过这个常量会随着实际的运行情况而决定,那么,readonly常量将会是一个不错的选择,例如上面第一个例子的订单号Order.ID。
  • 另外,如果要表示对象内部的默认值的话,而这类值通常是常量性质的,那么也可以考虑const。更多时候我们对源代码进行重构时(使用Replace Magic Number with Symbolic Constant),要去除魔数(Magic Number)的影响都会借助于const的这种特性。
  • 对于readonly和const所修饰的变量究竟是属于类级别的还是实例对象级别的问题,我们先看看如下代码:

Effective C++ (2) C#中的Const和Readonly_数据库_75Effective C++ (2) C#中的Const和Readonly_c++_76Using directives#region Using directives

Effective C++ (2) C#中的Const和Readonly_c++_77

Effective C++ (2) C#中的Const和Readonly_引用类型_78using System;

Effective C++ (2) C#中的Const和Readonly_初始化_79using System.Collections.Generic;

Effective C++ (2) C#中的Const和Readonly_引用类型_80using System.Text;

Effective C++ (2) C#中的Const和Readonly_构造函数_81

Effective C++ (2) C#中的Const和Readonly_c++_82#endregion

Effective C++ (2) C#中的Const和Readonly_数据库_83

Effective C++ (2) C#中的Const和Readonly_初始化_84namespace ConstantLab

Effective C++ (2) C#中的Const和Readonly_数据库_85Effective C++ (2) C#中的Const和Readonly_构造函数_86Effective C++ (2) C#中的Const和Readonly_c++_87{

Effective C++ (2) C#中的Const和Readonly_引用类型_88 class Program

Effective C++ (2) C#中的Const和Readonly_构造函数_89Effective C++ (2) C#中的Const和Readonly_初始化_90Effective C++ (2) C#中的Const和Readonly_引用类型_91{

Effective C++ (2) C#中的Const和Readonly_c++_92 static void Main(string[] args)

Effective C++ (2) C#中的Const和Readonly_构造函数_93Effective C++ (2) C#中的Const和Readonly_引用类型_94Effective C++ (2) C#中的Const和Readonly_c++_95{

Effective C++ (2) C#中的Const和Readonly_数据库_96           Constant c = new Constant(3);

Effective C++ (2) C#中的Const和Readonly_数据库_97           Console.WriteLine("ConstInt = " + Constant.ConstInt.ToString());

Effective C++ (2) C#中的Const和Readonly_引用类型_98           Console.WriteLine("ReadonlyInt = " + c.ReadonlyInt.ToString());

Effective C++ (2) C#中的Const和Readonly_c++_99           Console.WriteLine("InstantReadonlyInt = " + c.InstantReadonlyInt.ToString());

Effective C++ (2) C#中的Const和Readonly_数据库_100           Console.WriteLine("StaticReadonlyInt = " + Constant.StaticReadonlyInt.ToString());

Effective C++ (2) C#中的Const和Readonly_数据库_101

Effective C++ (2) C#中的Const和Readonly_初始化_102           Console.WriteLine("Press any key to continue");

Effective C++ (2) C#中的Const和Readonly_初始化_103           Console.ReadLine();

Effective C++ (2) C#中的Const和Readonly_数据库_104       }

Effective C++ (2) C#中的Const和Readonly_c++_105   }

Effective C++ (2) C#中的Const和Readonly_引用类型_106

Effective C++ (2) C#中的Const和Readonly_数据库_107 class Constant

Effective C++ (2) C#中的Const和Readonly_初始化_108Effective C++ (2) C#中的Const和Readonly_c++_109Effective C++ (2) C#中的Const和Readonly_初始化_110{

Effective C++ (2) C#中的Const和Readonly_c++_111 public Constant(int instantReadonlyInt)

Effective C++ (2) C#中的Const和Readonly_c++_112Effective C++ (2) C#中的Const和Readonly_引用类型_113Effective C++ (2) C#中的Const和Readonly_构造函数_114{

Effective C++ (2) C#中的Const和Readonly_c++_115           InstantReadonlyInt = instantReadonlyInt;

Effective C++ (2) C#中的Const和Readonly_数据库_116       }

Effective C++ (2) C#中的Const和Readonly_初始化_117

Effective C++ (2) C#中的Const和Readonly_构造函数_118 public const int ConstInt = 0;

Effective C++ (2) C#中的Const和Readonly_引用类型_119

Effective C++ (2) C#中的Const和Readonly_构造函数_120 public readonly int ReadonlyInt = 1;

Effective C++ (2) C#中的Const和Readonly_数据库_121

Effective C++ (2) C#中的Const和Readonly_数据库_122 public readonly int InstantReadonlyInt;

Effective C++ (2) C#中的Const和Readonly_数据库_123

Effective C++ (2) C#中的Const和Readonly_初始化_124 public static readonly int StaticReadonlyInt =4;

Effective C++ (2) C#中的Const和Readonly_c++_125   }

Effective C++ (2) C#中的Const和Readonly_构造函数_126}

  • 使 用VisualC#在Main()里面使用IntelliSence插入Constant的相关field的时候,发现ReadonlyInt和 InstantReadonlyInt需要指定Constant的实例对象;而ConstInt和StaticReadonlyInt却要指定 Constant class(参见上面代码)。可见,用const或者static readonly修饰的常量是属于类级别的;而readonly修饰的,无论是直接通过赋值来初始化或者在实例构造函数里初始化,都属于实例对象级别。
  • 一般情况下,如果你需要表达一组相关的编译时确定常量,你可以考虑使用枚举类型(enum),而不是把多个const常量直接嵌入到class中作为field,不过这两种方式没有绝对的孰优孰劣之分。