您的位置:首页 > 编程语言 > C#

const和readonly区别 (C# .net)

2010-04-04 16:57 489 查看
我们都知道,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 B = 10; static readonly A = B * 20;
4. static readonly int [] constIntArray = new int[] {1, 2, 3};
5. void SomeFunction()
{
const int a = 10;
...
}
6.private static string astr="abcd";
private const string str = astr+"efg";
1:不可以换成const。new操作符是需要执行构造函数的,所以无法在编译期间确定
2:可以换成const。我们也看到,Reference类型的常量(除了String)只能是Null。
3:可以换成const。我们可以在编译期间很明确的说,A等于200。
4:不可以换成const。道理和1是一样的,虽然看起来1,2,3的数组的确就是一个常量。
5:不可以换成readonly,readonly只能用来修饰类的field,不能修饰局部变量,也不能修饰property等其他类成员。

6.错误:如果在astr前加上const或者const改为readonly即可;

总结:1.const、readonly和static readonly定义的常量,指定初始值后(包括在构造函数内指定的初始值) 将不可更改,可读不可写;
2.const定义时必须指定初始值,而readonly定义时可以不进行初始化(MS建议在定义时初始值),同时也可以在构造函数内指定初始值,

并以构造函数内指定的值为准;

3.const和static readonly定义的常量是静态的,只能由类直接访问;而readonly定义的常量是非静态 的,只能由实例对象访问;
4.static readonly常量,如果在构造函数内指定初始值,则必须是静态无参构造函数;
5.const是编译时常量,readonly是运行时常量;cosnt较高效,readonly较灵活。在应用上以static readonly代替const,以平衡const在灵活性上的不足,
同时克服编译器优化cosnt性能,所带来的程序集引用不一致问题;

文章2:

readonly和const比较
前天犯了个低级错误,是关于readonly的,总结了一下:
C#的readonly关键字只能在字段上面使用
public readonly TcpClient client;
不能在类,方法,属性上面使用readonly!!
顺便看了一下readonly和const的区别:

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结合一起使用,该常量的值将由整个类的所有实例对象共同分享(详细论述参见后面的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:

using System;

public class Order
{
public Order()
{
Guid guid = Guid.NewGuid();
ID = guid.ToString("D");
}

// 对于每一份订单,其订单序号都是实时确定的常量。
public readonly string ID;

public override string ToString()
{
return "Order ID: " + ID;
}
}
Explaintion:

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

Remarks:

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

Using directives#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace ConstantLab
{
class Program
{
static void Main(string[] args)
{
Constant c = new Constant(3);
Console.WriteLine("ConstInt = " + Constant.ConstInt.ToString());
Console.WriteLine("ReadonlyInt = " + c.ReadonlyInt.ToString());
Console.WriteLine("InstantReadonlyInt = " + c.InstantReadonlyInt.ToString());
Console.WriteLine("StaticReadonlyInt = " + Constant.StaticReadonlyInt.ToString());

Console.WriteLine("Press any key to continue");
Console.ReadLine();
}
}

class Constant
{
public Constant(int instantReadonlyInt)
{
InstantReadonlyInt = instantReadonlyInt;
}

public const int ConstInt = 0;

public readonly int ReadonlyInt = 1;

public readonly int InstantReadonlyInt;

public static readonly int StaticReadonlyInt = 4;
}
}

使用Visual C#在Main()里面使用IntelliSence插入Constant的相关field的时候,发现ReadonlyInt和InstantReadonlyInt需要指定Constant的实例对象;而ConstInt和StaticReadonlyInt却要指定Constant class(参见上面代码)。可见,用const或者static readonly修饰的常量是属于类级别的;而readonly修饰的,无论是直接通过赋值来初始化或者在实例构造函数里初始化,都属于实例对象级别。
一般情况下,如果你需要表达一组相关的编译时确定常量,你可以考虑使用枚举类型(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来对代码进行重构。(有关多态的详细介绍,请参见《今天你多态了吗?》一文。)
Comments:

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

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/powerglover/archive/2009/07/15/4350247.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: