const 在编译时初始化,其余在运行时初始化:在类调用时静态,其他字段(非静态)在实例创建时初始化。 问题:为什么 const 在编译时初始化?它们是静态的,为什么不能在静态构造函数中为它们赋值?
一般来说这是如何发生的? 如果const被初始化,那么就需要参考类来理解const原则上存在于其内部并读取它们的值,因此static也应该被计算并初始化,以及静态构造函数。但显然这不会发生,所以没有对该类的引用?那么 const 的值是如何读取的,为什么我们不能在不引用类的情况下访问它们呢? 例如:Console.WriteLine(MyClass.PI)? 为什么我们不能:Console.WriteLine(PI)?因为不知道PI在哪里。那么编译器在不知道 PI 在哪里、原则上也不知道 PI 存在的情况下,如何在不访问类的情况下在编译阶段输入值呢?
有多种方法可以实现这一点,但在 .Net 下的 C# 中,常量会在代码转换为 IL 代码(类似于汇编语言,成为 .Net 实际运行的字节码)之前插入代码中。 .
我使用了一个名为 LINQPad8 的工具来分析这种情况的一个简单示例:
class Program
{
private const int VALUE = 10;
static void Main(string[] args)
{
int value1 = 10;
Console.WriteLine(value1);
int value2 = VALUE;
Console.WriteLine(value2);
Console.WriteLine(VALUE);
}
}
在使用 LINQPad 用来完成工作的 .Net 工具 ILSpy 分析代码之前,该常量已经从代码中进行了预处理,生成以下内容:
int value1 = 10;
Console.WriteLine (value1);
int value2 = 10;
Console.WriteLine (value2);
Console.WriteLine (10);
所有这些都变成了以下 CLR IL 代码:
IL_0000 nop
IL_0001 ldc.i4.s 0A // 10
IL_0003 stloc.0 // value1
IL_0004 ldloc.0 // value1
IL_0005 call Console.WriteLine (Int32)
IL_000A nop
IL_000B ldc.i4.s 0A // 10
IL_000D stloc.1 // value2
IL_000E ldloc.1 // value2
IL_000F call Console.WriteLine (Int32)
IL_0014 nop
IL_0015 ldc.i4.s 0A // 10
IL_0017 call Console.WriteLine (Int32)
IL_001C nop
IL_001D ret
特别是在最后一次调用中,常量 VALUE 直接用作调用 Console.WriteLine(Int32) 的参数,没有访问在内存中存储变量的指令,即对 stloc 和 ldloc 的调用。
整数 0x0A(十进制为 10)作为 32 位有符号整数直接压入堆栈,这就是向方法调用提供参数的方式。 另外两个调用显示了使用变量并将其压入堆栈时的外观。