c#的const和readonly使用小结

简介:

1、应用场景重现

一个简单的应用程序解决方案,如下:

csharpconstant

其中,类库CSharpLib里定义一个简单的类SomeType如下:

1
2
3
4
5
6
7
8
namespace  CSharpLib
{
     public  class  SomeType
     {
         public  const  int  ConstField = 50;
         public  static  readonly  int  ReadonlyField = 50;
     }
}

在控制台应用程序ConsoleApp中,引用类库CSharpLib,然后写下如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using  System;
 
namespace  ConsoleApp
{
     using  CSharpLib;
 
     class  Program
     {
         static  void  Main( string [] args)
         {
             Console.WriteLine( "Const field is {0}." , SomeType.ConstField);
             Console.WriteLine( "Readonly field is {0}." , SomeType.ReadonlyField);
             Console.ReadKey();
         }
     }
}

这样这个控制台应用程序的输出就都是50,这个结果应该是每个开发人员都预期的,没有任何可疑之处。

当我们把类库CSharpLib中的常量都改变时:

1
2
3
4
5
6
7
8
namespace  CSharpLib
{
     public  class  SomeType
     {
         public  const  int  ConstField = 55;
         public  static  readonly  int  ReadonlyField = 5555;
     }
}

也就是说,我们预期的目标输出是55和5555。当改好类库,开发人员发现控制台应用程序没做出任何修改,所以只将类库重新生成,然后将.dll文件拷到控制台应用程序bin目录下(做过asp.net开发的应该都知道,当某个类库修改时,我们通常都直接拷贝修改后的类库.dll到网站下的bin目录,除了习惯性偷懒,版本控制也是一个问题),然后双击运行ConsoleApp.exe文件,我们惊讶地发现当前的输出不是55和5555,而是50和5555,也就是说,const字段修改后没有达到预期目的。

 

2、注意点总结

为什么会发生上面的现象呢?从c#的常量的本质说起。

(1)、什么是常量(书中原话摘录)

“常量(constant)是一个特殊的符号,它是一个从不变化的值。定义常量符号时,它的值必须能在编译时确定。确定之后,编译器将常量的值保存到程序集的元数据中。常量总是被视为静态成员,而不是实例成员。代码引用一个常量符号时,编译器会在定义常量的程序集的元数据中查找该符号,提取常量的值,并将值嵌入生成的IL代码中。由于常量的值直接嵌入代码,所以在运行时,不需要为常量分配内存。除此之外,不能获取常量的地址,也不能以传引用的方式传递常量。这些限制同时意味着,常量没有很好的跨程序集版本控制特性。“

(2)、静态常量和动态常量

简单来说,用const修饰的就是静态常量(compile-time constants),而且它是隐式静态的。动态常量(runtime constants)的值是在运行时获得的,IL中将其标为只读常量,而不是用常量的值代替。

下面对二者的特点进行简单比较:

 

静态常量

动态常量

内存分配

可使用类型

较少的C#基元类型,如String,Int32等等

任意类型

初始化赋值 声明变量时同时赋值 可在构造函数中赋值
何时发挥作用 编译时进行替换,可生成元数据 相当于类中的数据成员

通过上述分析,我们可以理解1中所产生的输出不一致现象,是因为const字段ConstField已经嵌入到控制台应用程序的IL代码中(获取ConstField字段并不从类库的dll文件中加载),从IL中我们可以看得很清楚:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.method private  hidebysig static  void   Main( string [] args) cil managed
{
   .entrypoint
   // 代码大小       47 (0x2f)
   .maxstack  8
   IL_0000:  nop
   IL_0001:  ldstr      "Const field is {0}."
   IL_0006:  ldc.i4.s   50  //<span style="color: #ff0000;">const字段没有变化</span>
   IL_0008:  box        [mscorlib]System.Int32
   IL_000d:  call       void  [mscorlib]System.Console::WriteLine( string ,
                                                                 object )
   IL_0012:  nop
   IL_0013:  ldstr      "Readonly field is {0}."
   IL_0018:  ldsfld     int32 [CSharpLib]CSharpLib.SomeType::ReadonlyField
   IL_001d:  box        [mscorlib]System.Int32
   IL_0022:  call       void  [mscorlib]System.Console::WriteLine( string ,
                                                                 object )
   IL_0027:  nop
   IL_0028:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
   IL_002d:  pop
   IL_002e:  ret
} // end of method Program::Main

而不是像static readonly字段一样从内存加载类库程序集。如果应用程序需获取新值,也必须重新编译,或者使用readonly字段。

 

3、类比问题

我记得以前看过的一篇博文,是讨论枚举使用中输出不一致情况的,google了一下。您可以参考这一篇小心枚举陷阱进行比较,如果您对IL有了解,可以简单从IL着手分析对比,更能加深理解。

 

总结:const和readonly在编程实践中经常用到,理解它们的基本原理,对于开发和维护都是非常有必要的。在类库开发定义常量的时候,个人偏向于使用static readonly组合关键字。







本文转自JeffWong博客园博客,原文链接:http://www.cnblogs.com/jeffwongishandsome/archive/2010/11/01/1866642.html,如需转载请自行联系原作者


目录
相关文章
|
C# C++
c#调用c++开发的dll const char* 返回值接收问题
原文:c#调用c++开发的dll const char* 返回值接收问题 用c#调用视频接口相关的dll,dll使用c++开发。 c++接口定义如下: PLATFORM const char* Plat_GetValueStr(const char* propertyName, int iU...
2391 0
|
C# 编译器
1.2 - C#语言习惯 - 用运行时常量readonly而不是编译期常量const
  C#中有两种类型的常量:编译期常量和运行时常量。二者有着截然不同的行为,使用不当将会带来性能上或正确性上的问题。   这两个问题最好都不要发生,不过若难以同时避免的话,那么一个略微慢一些但能保证正确的程序则要好过一个快速但不能正常工作的程序。
1008 0
|
C#
c#中const与readonly区别
原文:c#中const与readonly区别 const 的概念就是一个包含不能修改的值的变量。常数表达式是在编译时可被完全计算的表达式。因此不能从一个变量中提取的值来初始化常量。如果 const int a = b+1;b是一个变量,显然不能再编译时就计算出结果,所以常量是不可以用变量来初始化的。
739 0
|
C#
[c#基础]关于const和readonly常见的笔试题剖析
原文:[c#基础]关于const和readonly常见的笔试题剖析 引言 有那么几天没更新博客了,发现到了不得不写的地步,总是有那么个声音在强迫自己,虽然工作很累,但是有些东西不写出来,不能原谅自己。
1261 0
|
C# 编译器
C#基础知识系列八(const和readonly关键字)
前言   不知道大家对const和readonly这两个关键字的区别有什么了解,原来自己之前还真不清楚它们到底是怎么回事,那么如果你也不是很清楚的话,可以一起来探讨一下。在了解这两个关键字的时候我们先来了解一下静态常量和动态常量。
926 0
|
1月前
|
C#
24. C# 编程:用户设定敌人初始血值的实现
24. C# 编程:用户设定敌人初始血值的实现
18 0