有很多关于这个主题的问题,但除了 一个非常简短的问题之外,没有一个问题涉及以下情况。
来自C# 4书:
Marc 也写道:
如果您更改常量的值,则需要重新构建所有客户端。
问题:
1)为什么? static readonly
和 const
都是 static
的吗?
2)实际上,值保存在哪里?
3)使字段 static readonly
究竟是如何 "在幕后" 解决这个问题的?
有很多关于这个主题的问题,但除了 一个非常简短的问题之外,没有一个问题涉及以下情况。
来自C# 4书:
Marc 也写道:
如果您更改常量的值,则需要重新构建所有客户端。
问题:
1)为什么? static readonly
和 const
都是 static
的吗?
2)实际上,值保存在哪里?
3)使字段 static readonly
究竟是如何 "在幕后" 解决这个问题的?
不,const就是const,不是static。它是一种特殊情况,有着不同的规则;它仅在编译时设置(而非运行时),并且处理方式也不同。
关键在于以下内容的含义:
var foo = SomeType.StaticValue;
对比
var bar = SomeType.ConstValue;
在第一种情况下,它从SomeType
中在运行时读取值,即通过ldsfld
; 然而,在第二种情况下,它被编译成该值,也就是说,如果ConstValue
恰好等于123
,那么第二种情况与以下内容相同:
var bar = 123;
在运行时,由于值123
已经被编译器评估并存储,因此它来自SomeType
的事实是不存在的。因此需要重新构建以获取新值。
将其更改为static readonly
意味着“从SomeType
加载值”的操作得以保留。
所以以下代码:
static int Foo()
{
return Test.Foo;
}
static int Bar()
{
return Test.Bar;
}
...
static class Test
{
public static readonly int Foo = 123;
public const int Bar = 456;
}
编译后的结果如下:
.method private hidebysig static int32 Bar() cil managed
{
.maxstack 8
L_0000: ldc.i4 0x1c8
L_0005: ret
}
.method private hidebysig static int32 Foo() cil managed
{
.maxstack 8
L_0000: ldsfld int32 ConsoleApplication2.Test::Foo
L_0005: ret
}
请注意,在Bar
中,ldc
是直接加载一个值(0x1c8 == 456),完全没有使用Test
。.field public static literal int32 Bar = int32(0x1c8)
.field public static initonly int32 Foo
.field ... static
的形式实现,在C#中则通过 TypeName.ConstName
访问。但重要的是语义;就语义而言,访问常量成员与访问常规静态成员(字段/属性)之间有很大的区别。 - Marc Gravell1) const
在编译时使用您提供的值进行解析。而static readonly
是一个静态变量。
2) static
值通常存储在堆上的特殊区域中,称为高频堆。正如我之前所说,常量在编译时被替换。
3) 将其设置为static readonly
将解决问题,因为您将在运行时读取变量值,而不是编译时提供的值。
const
字段将被编译(“内联”)到汇编中-就像简单的搜索和替换一样。 static readonly
表示一个普通字段,不允许更改,并且仅存在于内存中一次,但仍然由内存位置引用。