透过IL看C# (外一篇)——警惕常量陷阱
透过IL看C# (外一篇)
警惕常量陷阱
原文地址:http://www.cnblogs.com/AndersLiu/archive/2008/11/23/csharp-via-il-constant-a.html
原创:Anders Liu
摘要:常量的含义本是“永远不会变的量”,但是如果作为类库开发人员,把常量用作“可以由我变,但不能由你变”的量,那就可能铸成大错了。
下面是老刘写的一个类库中的一个类:
代码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#