2010-12-10 118 views
13

今天我有点惊讶,当我在一个静态类改变一个公开可见的常数的值,然后更换组件的旧副本与新编译版本。令人惊讶的是,引用该程序集的现有程序没有拿出该常量的新值。也就是说,我没有重新编译可执行文件,而只是替换了一个程序集。在.NET中,为什么常量是在编译时而不是在JIT时间进行评估的?

我的实验的完整描述为How constant is a constant?

我会承认自己的这种行为感到非常惊讶。我明白这是怎么回事,但我不明白为什么。是否有一个特殊的技术原因,为什么常量不能在JIT时间而不是编译时间?有没有这样做会破坏事情的情况?

+0

这是书太多了,博客讨论的行为,所以它事实上不是:) – 2010-12-10 23:56:54

+0

@Lex让人吃惊:奇怪的是,我我以前从未讨论过它。虽然我仍然怀疑*为什么*。 – 2010-12-10 23:59:47

+1

我仍然没有得到将它烘烤成使用组件的优点。 – CodesInChaos 2010-12-11 10:50:00

回答

30

常量应该是不变。对于所有时间。常量是像pi的值或铅原子中的质子数量。

如果你不断变化,它不是一个真正的恒定;改用只读字段。

还看到框架设计指南,其中规定:

使用永远不变的常量不变领域。编译器将const字段的值直接转化为调用代码。因此,如果没有破坏兼容性的风险,const值永远不会被改变。

从本质上讲,改变一个常量而不重新编译依赖于它的所有东西都会破坏改变方法的签名而不重新编译依赖它的所有东西。当编译依赖程序集时,编译器会“烘烤”关于引用程序集中元数据信息的各种假设。如果你做任何改变,你不能指望的东西简单地继续工作。

+0

JIT编译器是否评估只读字段,然后在编译为本机代码时将它们视为常量?也就是说,使用常量和使用只读字段之间是否存在性能差异? – 2010-12-10 23:43:48

+5

@Jim:我不知道。首先,有好几个或更多的JIT编译器,而且我都不是专家。其次,JIT编译器经常会根据运行时发生的事情来改变他们的行为,比如调试器是否连接。第三,如果您关心性能,请双向编写代码,运行它,然后查看是否可以衡量差异。如果差异太小而无法衡量,那么这可能不是什么区别,你应该首先担心。 – 2010-12-10 23:45:42

+7

我听说PI现在正好是3. – ChaosPandion 2010-12-10 23:48:49

1

也有申报“常量”第三条道路:一个公共静态属性。

public static string ConstString {get{return "First test";}} 

这具有一个只读字段的版本语义,但是如果抖动内联吸气它成为JIT-时间常数。与const不同,它可以用于用户定义的类型。

我认为这是使用静态属性的值类型和字符串是一个好主意,但不适用于用户定义的类,因为你不希望在每个属性的访问分配一个新的实例。

我用这个在我的定点类型是这样的:

public struct FixedPoint 
{ 
    private int raw; 
    private const fracDigits=16; 

    private FixedPoint(int raw) 
    { 
    this.raw=raw; 
    } 

    public static FixedPoint Zero{get{return new FixedPoint();}} 
    public static FixedPoint One{get{return new FixedPoint(1<<fracDigits);}} 
    public static FixedPoint MaxValue{get{return new FixedPoint(int.MaxValue);}} 
} 
相关问题