C# 的readonly和const区别1

导读:
  C#引入了readonly修饰符来表示只读域,const来表示不变常量。顾名思义对只读域不能进行写操作,不变常量不能被修改,这两者到底有什么区别呢?只读域只能在初始化--声明初始化或构造器初始化--的过程中赋值,其他地方不能进行对只读域的赋值操作,否则编译器会报错。只读域可以是实例域也可以是静态域。只读域的类型可以是C#语言的任何类型。但const修饰的常量必须在声明的同时赋值,而且要求编译器能够在编译时期计算出这个确定的值。const修饰的常量为静态变量,不能够为对象所获取。const修饰的值的类型也有限制,它只能为下列类型之一(或能够转换为下列类型的):sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, enum类型, 或引用类型。值得注意的是这里的引用类型,由于除去string类型外,所有的类型出去null值以外在编译时期都不能由编译器计算出他们的确切的值,所以我们能够声明为const的引用类型只能为string或值为null的其他引用类型。显然当我们声明一个null的常量时,我们已经失去了声明的意义--这也可以说是C#设计的尴尬之处!
  这就是说,当我们需要一个const的常量时,但它的类型又限制了它不能在编译时期被计算出确定的值来,我们可采取将之声明为static readonly来解决。但两者之间还是有一点细微的差别的。看下面的两个不同的文件:
  //file1.cs
//csc /t:library file1.cs
using System;
namespace MyNamespace1
{
public class MyClass1
{
public static readonly int myField = 10;
}
}
//file2.cs
//csc /r:file1.dll file2.cs
using System;
namespace MyNamespace2
{
public class MyClass1
{
public static void Main()
{
Console.WriteLine(MyNamespace1.MyClass1.myField);
}
}
}

  我们的两个类分属于两个文件file1.cs 和file2.cs,并分开编译。在文件file1.cs内的域myField声明为static readonly时,如果我们由于某种需要改变了myField的值为20,我们只需重新编译文件file1.cs为file1.dll,在执行file2.exe时我们会得到20。但如果我们将static readonly改变为const后,再改变myField的初始化值时,我们必须重新编译所有引用到file1.dll的文件,否则我们引用的MyNamespace1.MyClass1.myField将不会如我们所愿而改变。这在大的系统开发过程中尤其需要注意。实际上,如果我们能够理解const修饰的常量是在编译时便被计算出确定的值,并代换到引用该常量的每一个地方,而readonly时在运行时才确定的量--只是在初始化后我们不希望它的值再改变,我们便能理解C#设计者们的良苦用心,我们才能彻底把握const和readonly的行为!
  ---------------------
   Features:
  readonly和const都是用来标识常量的[1]。
  const可用于修饰class的field或者一个局部变量(local variable);而readonly仅仅用于修饰class的field。
  const常量的值必定在编译时就已明确并且恒定的;而readonly常量却有一点不同,那就是其值可以在运行时编译,当然,它也必须遵守作为常量的约束,那就是值必须恒定不变。
  const常量必须在声明的同时对其进行赋值,并且确保该值在编译时可确定并恒定;而readonly常量则可以根据情况选择在声明的同时对其赋予一个编译时确定并恒定的值,或者将其值的初始化工作交给实例构造函数(instant constructor)完成。如: public readonly stringm_Now = DateTime.Now.ToString();,m_Now会随着运行时实际情况变化而变化。
  const常量属于类级别(class level)而不是实例对象级别(instant object level),并且它不能跟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的值具有只读特性。有关字符串恒定性的内容,可以参考《Microsoft .NET框架程序设计(修订版)》。
   Examples:
  
  
  usingSystem;
  
  
  
  publicclassOrder
  
  
  
  
  
  {
  
  
  publicOrder()
  
  
  
  
  
  
  {
  
  
  Guid guid = Guid.NewGuid();
  
  
  ID = guid.ToString("D");
  
  
  }
  
  
  
  // 对于每一份订单,其订单序号都是实时确定的常量。
  
  publicreadonlystringID;
  
  
  
  
  publicoverridestringToString()
  
  
  
  
  
  
  {
  
  
  return"Order ID: " + ID;
  
  
  }
  
  }
  Explaintion:
  如果结合数据库使用,ID field通常都会都会与某个表的主健(primary key)关联起来,如Orders表的OrderID。
  数据库的主健通常采用以下三种方式:
  自动递增值。你可以通过把DataColumn.AutoIncrement设定为true值来激活自动递增特性。
  唯一名称。这个是使用自己定义的算法来生成一个唯一序列号。
  GUID(全局唯一标识符)。你可以通过System.Guid结构来生成GUID,如上例。
  
  
  usingSystem;
  
  
  
  classCustomer
  
  
  
  
  
  {
  
  
  publicCustomer(stringname, intkind)
  
  
  
  
  
  
  {
  
  
  m_Name =name;
  
  
  m_Kind =kind;
  
  
  }
  
  
  
  publicconstintNORMAL =0
  
  publicconstintVIP =1
  
  publicconstintSUPER_VIP =2
  
  
  
  privatestringm_Name;
  
  
  publicstringName
  
  
  
  
  
  
  {
  
  
  
  
  get
  
  { returnm_Name; }
  
  }
  
  
  
  privatereadonlyintm_Kind;
  
  
  publicintKind
  
  
  
  
  
  
  {
  
  
  
  
  get
  
  { returnm_Kind; }
  
  }
  
  
  
  publicoverridestringToString()
  
  
  
  
  
  
  {
  
  
  if(m_Kind ==SUPER_VIP)
  
  
  return"Name: "+m_Name +"[SuperVip]"
  
  elseif(m_Kind ==VIP)
  
  
  return"Name: "+m_Name +"[Vip]"
  
  else
  
  return"Name: "+m_Name +"[Normal]"
  
  }
  
  }
   Remarks:
  一般情况下,如果你需要声明的常量是普遍公认的并作为单个使用,例如圆周率,黄金分割比例等。你可以考虑使用const常量,如: public const doublePI = 3.1415926;。如果你需要声明常量,不过这个常量会随着实际的运行情况而决定,那么,readonly常量将会是一个不错的选择,例如上面第一个例子的订单号Order.ID。
  另外,如果要表示对象内部的默认值的话,而这类值通常是常量性质的,那么也可以考虑const。更多时候我们对源代码进行重构时(使用Replace Magic Number with Symbolic Constant),要去除魔数(Magic Number)的影响都会借助于const的这种特性。
  对于readonly和const所修饰的变量究竟是属于类级别的还是实例对象级别的问题,我们先看看如下代码:
  使用Visual C#在Main()里面使用IntelliSence插入Constant的相关field的时候,发现ReadonlyInt和InstantReadonlyInt需要指定Constant的实例对象;而ConstInt和StaticReadonlyInt却要指定Constant class(参见上面代码)。可见,用const或者static readonly修饰的常量是属于类级别的;而readonly修饰的,无论是直接通过赋值来初始化或者在实例构造函数里初始化,都属于实例对象级别。
  一般情况下,如果你需要表达一组相关的编译时确定常量,你可以考虑使用枚举类型(enum),而不是把多个const常量直接嵌入到class中作为field,不过这两种方式没有绝对的孰优孰劣之分。

本文转自
http://hi.baidu.com/wwd252/blog/item/06df82ef50eff915fcfa3c75.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值