透过IL看C# (外一篇)——警惕常量陷阱

透过IL看C# (外一篇)
警惕常量陷阱

摘要:常量的含义本是“永远不会变的量”,但是如果作为类库开发人员,把常量用作“可以由我变,但不能由你变”的量,那就可能铸成大错了。

下面是老刘写的一个类库中的一个类:

代码1 - 老刘的“类库”

其中的常量Version表示类库的当前版本,而方法GetVersion会给出描述当前版本的字符串。用下面的命令行可以将其编译为一个DLL:

csc /t:library Library.cs

接下来,一个倒霉的家伙从老刘这买了这个类库,写了自己的程序:

代码2 - 老刘的“消费者”

这个家伙用下面的命令行编译了自己的程序:

csc /r:Library.dll Program.cs

运行程序,一切正常,屏幕显示出预期的结果:

Library Version: 1
You are currently using version 1.

过了两天,老刘升级代码了。都升级哪些部分了呢?把Version的值改成2了,然后重新编译了Library。所以代码就不贴了。前面这个家伙因为很乖,从老刘这里买了正版,所以老刘承诺免费升级50次,因此他拿到了新版的Library.dll。

当这个家伙再跑自己的程序(注意他没有重新编译自己的代码),问题来了:

Library Version: 1
You are currently using version 2.

我们看到,通过常量访问得到的版本依然是1,而通过类库方法得到的版本字符串是2。

这是怎么回事呢?让我们祭出ILDasm,看一下Version常量的定义:

代码3 - IL中的常量定义

Version定义中的关键字literal表明,这是一个字面常量值。“字面”意味着其值将被直接编译到IL代码中,而不会保留对这个常量的引用。

因此,当我们看到Program.cs中Main方法的IL代码后,就不那么吃惊了:

代码4 - 引用Version常量部分的IL代码

我们可以看到,这里根本没有Version这个常量的影子。只有IL_0006: ldc.i4.1这一行直接加载了数值1——在编译Program的时候,类库中Version常量的值。

有朋友可能想说,那重新编译一下Program不就解决问题了?可问题在于,.NET的一个重要原则就是部署容易和消除DLL陷阱。不能强制要求客户在类库升级后还要重新编译自己的程序。

所以,对于类库设计人员来说,当暴露一个公开的常量时要非常小心,只有确信一个值永远不会发生变化(包括今后的一系列升级)时,才能使用常量。否则请使用只读(只有get访问器)属性或只读(readonly)字段来返回这样的值。

返回目录:透过IL看C#

posted @ 2008-11-23 10:05  Anders Liu  阅读(4712)  评论(38编辑  收藏  举报