为什么在编译时将const
用法替换为它们的值,但仍然包含在程序集中?至少这是IL DASM和Reflector所展示的。
目前我正在使用const
来定义许多幻数和字符串,以便更容易地更改代码而不会影响运行时性能。
现在我知道这不会影响已用内存,但它仍会影响编译的程序集大小,这对于手机应用程序来说至关重要。
另一个缺点是,在查看反汇编代码时,其他人更容易理解幻数。
我真的很感兴趣为什么编译器(Mono和.NET)确实这样做了?
答案 0 :(得分:6)
此行为在ECMA-335 standard中指定(其中.NET和Mono都是实现)。引用第II.22.9节“常数”:
请注意,
Constant
信息不会直接影响运行时行为,尽管它通过Reflection可见(因此可用于实现System.Enum.ToString
提供的功能)。编译器在编译时在导入元数据时检查此信息,但常量本身的值(如果使用)将嵌入到编译器发出的CIL流中。在运行时没有CIL指令可以访问Constant
表。
也就是说,const
值是“内联的”(可能是因为它们可能是出于性能原因),但仍保留在元数据中,因此编译器和工具可以对它们进行检查。
如果没有为const
- 字段发出元数据,这将产生这些后果(可能还有其他 - 这只是两个例子):
const
字段。System.Reflection
检查某个字段,则无法再使用FieldInfo.IsLiteral
property。答案 1 :(得分:2)
增加的程序集大小基本上是由于C#编译器发出了关于const
的其他元数据。
您对此短节目的期望是什么?
class Program
{
public const int C = 0;
public int F = 0;
static void Main(string[] args)
{
foreach (FieldInfo field in typeof(Program).GetFields())
{
Console.WriteLine("{0}: IsLiteral = {1}", field.Name, field.IsLiteral);
}
}
}
实际输出是:
C: IsLiteral = True
F: IsLiteral = False
与C#源中的声明完全匹配:两个字段,其中一个const
。
现在让我们假设C#编译器决定不发出Constant
元数据。然后输出将是:
C: IsLiteral = False
F: IsLiteral = False
与C#源代码相比,这显然是不正确的,因为现在这两个字段都显示为非const
。
最后,假设C#编译器决定不发布C
的任何元数据(因为它“无论如何”内联“了字段的值):
F: IsLiteral = False
这也是不正确的,因为反射不再报告存在C#源代码中明显存在的字段。至少对我来说,这将是一个令人兴奋的好时刻。
这些反例应该明确为什么即使对于const
字段也会发出完整的元数据也是一件好事。