【转】readonly vs. const [C#]

readonly vs. const [C#]

Updated on Friday, October 29, 2004

Written by Allen Lee

Features:

  • readonlyconst都是用来标识常量的[1]
  • 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结合一起使用,该常量的值将由整个类的所有实例对象共同分享(详细论述参见后面的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常量的值可以有两种情况,stringnull。其实,string虽然是引用类型,但是.NET却对它特别处理,这种处理叫做字符串恒定性(immutable),使得string的值具有只读特性。有关字符串恒定性的内容,可以参考《Microsoft .NET框架程序设计(修订版)》

 

Examples:

None.gifusing System;
None.gif
None.gif
public class Order
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    
public Order()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        Guid guid = Guid.NewGuid();
InBlock.gif        ID = guid.ToString("D");
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
// 对于每一份订单,其订单序号都是实时确定的常量。
InBlock.gif
    public readonly string ID;
InBlock.gif
InBlock.gif    
public override string ToString()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
return "Order ID: " + ID;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

Explaintion:

  • 如果结合数据库使用,ID field通常都会都会与某个表的主健(primary key)关联起来,如Orders表的OrderID。
  • 数据库的主健通常采用以下三种方式:
    • 自动递增值。你可以通过把DataColumn.AutoIncrement设定为true值来激活自动递增特性。
    • 唯一名称。这个是使用自己定义的算法来生成一个唯一序列号。
    • GUID(全局唯一标识符)。你可以通过System.Guid结构来生成GUID,如上例。
None.gif using  System;
None.gif
None.gif
class
 Customer
ExpandedBlockStart.gifContractedBlock.gif
dot.gif
{
InBlock.gif    
public Customer(string name, int
 kind)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif
{
InBlock.gif        m_Name 
=
 name;
InBlock.gif        m_Kind 
=
 kind;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
public const int NORMAL = 0;
InBlock.gif    
public const int VIP = 1
;
InBlock.gif    
public const int SUPER_VIP = 2
;
InBlock.gif
InBlock.gif    
private string
 m_Name;
InBlock.gif    
public string
 Name
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get dot.gifreturn m_Name; }

ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
private readonly int m_Kind;
InBlock.gif    
public
 int Kind
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get dot.gifreturn m_Kind; }

ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
public override string ToString()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif
{
InBlock.gif        
if(m_Kind ==
 SUPER_VIP)
InBlock.gif            
return "Name: " + m_Name + "[SuperVip]"
;
InBlock.gif        
else if(m_Kind ==
 VIP)
InBlock.gif            
return "Name: " + m_Name + "[Vip]"
;
InBlock.gif        
else

InBlock.gif            
return "Name: " + m_Name + "[Normal]";
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

 


Remarks:

  • 一般情况下,如果你需要声明的常量是普遍公认的并作为单个使用,例如圆周率,黄金分割比例等。你可以考虑使用const常量,如:public const double PI = 3.1415926;。如果你需要声明常量,不过这个常量会随着实际的运行情况而决定,那么,readonly常量将会是一个不错的选择,例如上面第一个例子的订单号Order.ID。
  • 另外,如果要表示对象内部的默认值的话,而这类值通常是常量性质的,那么也可以考虑const。更多时候我们对源代码进行重构时(使用Replace Magic Number with Symbolic Constant),要去除魔数(Magic Number)的影响都会借助于const的这种特性。
  • 对于readonlyconst所修饰的变量究竟是属于类级别的还是实例对象级别的问题,我们先看看如下代码:
None.gifusing System;
None.gif
None.gif
namespace ConstantLab
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    
class Program
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
static void Main(string[] args)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            Constant c 
= new Constant(3);
InBlock.gif            Console.WriteLine(
"ConstInt = " + Constant.ConstInt.ToString());
InBlock.gif            Console.WriteLine(
"ReadonlyInt = " + c.ReadonlyInt.ToString());
InBlock.gif            Console.WriteLine(
"InstantReadonlyInt = " + c.InstantReadonlyInt.ToString());
InBlock.gif            Console.WriteLine(
"StaticReadonlyInt = " + Constant.StaticReadonlyInt.ToString());
InBlock.gif
InBlock.gif            Console.WriteLine(
"Press any key to continue");
InBlock.gif            Console.ReadLine();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
class Constant
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
public Constant(int instantReadonlyInt)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            InstantReadonlyInt 
= instantReadonlyInt;
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public const int ConstInt = 0;
InBlock.gif
InBlock.gif        
public readonly int ReadonlyInt = 1;
InBlock.gif
InBlock.gif        
public readonly int InstantReadonlyInt;
InBlock.gif
InBlock.gif        
public static readonly int StaticReadonlyInt = 4;
ExpandedSubBlockEnd.gif    }

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

 

None.gif using  System;
None.gif
None.gif
enum
 CustomerKind
ExpandedBlockStart.gifContractedBlock.gif
dot.gif
{
InBlock.gif    SuperVip,
InBlock.gif    Vip,
InBlock.gif    Normal
ExpandedBlockEnd.gif}

None.gif
None.gif
class  Customer
ExpandedBlockStart.gifContractedBlock.gif
dot.gif
{
InBlock.gif    
public Customer(string
 name, CustomerKind kind)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif
{
InBlock.gif        m_Name 
=
 name;
InBlock.gif        m_Kind 
=
 kind;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
private string m_Name;
InBlock.gif    
public string
 Name
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get dot.gifreturn m_Name; }

ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
private CustomerKind m_Kind;
InBlock.gif    
public
 CustomerKind Kind
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get dot.gifreturn m_Kind; }

ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
public override string ToString()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif
{
InBlock.gif        
return "Name: " + m_Name + "[" + m_Kind.ToString() + "]"
;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

 

  • 然而,当这种结合使用枚举和条件判断的代码阻碍了你进行更灵活的扩展,并有可能导致日后的维护成本增加,你可以代之以多态,使用Replace Conditional with Polymorphism来对代码进行重构。(有关多态的详细介绍,请参见《今天你多态了吗?》一文。)

 

Comments:

  • readonly field准确来说应该翻译成为“只读域”,这里是为了统一翻译用语才将它和const两者所修饰的量都说成“常量”,希望没有引起误会。

 原文URL:http://www.cnblogs.com/allenlooplee/archive/2004/10/23/55183.html

转载于:https://www.cnblogs.com/jeriffe/articles/1422601.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值