【问题标题】:.NET const affecting compiled assembly size.NET const 影响编译的程序集大小
【发布时间】:2013-04-06 09:46:27
【问题描述】:

为什么const 的用法在编译时被它们的值替换,但仍然包含在程序集中?至少这是 IL DASM 和 Reflector 所展示的。

目前我使用const 来定义许多幻数和字符串,以便更容易地更改代码而不影响运行时性能。

现在我知道这不会影响使用的内存,但它仍然会影响编译的程序集大小,这对于手机应用程序来说至关重要。

另一个缺点是其他人在查看反汇编代码时更容易理解幻数。

我真的很感兴趣为什么编译器(Mono 和 .NET)会这样做?

【问题讨论】:

标签: .net compilation constants


【解决方案1】:

此行为在ECMA-335 standard 中指定(.NET 和 Mono 都是其中的实现)。引用第 II.22.9 节,“常量”:

请注意,Constant 信息不会直接影响运行时行为,尽管它通过反射可见(因此可用于实现诸如 System.Enum.ToString 提供的功能)。编译器在导入元数据时在编译时检查此信息,但常量本身的值(如果使用)会嵌入到编译器发出的 CIL 流中。没有 CIL 指令可以在运行时访问Constant 表。

也就是说,const 值是“内联”的(可能是因为它们可以内联,并且出于性能原因),但仍然保存在元数据中,以便编译器和工具可以检查它们。

如果没有为字段的const-ness 发出元数据,则会产生以下后果(可能还有其他后果——这只是两个示例):

  • 编译器或 Reflector 等工具无法再区分常规字段和 const 字段。
  • 如果您使用 System.Reflection 检查字段,您将无法再使用 FieldInfo.IsLiteral property

【讨论】:

  • +1 我仍然不确定这会如何引起人们的注意,因为我没想到会通过反射找到const 字段。同样有趣的是System.Enum.ToString 可能是获取枚举字符串表示最慢的实现。但我想如果 ECMA 这么说的话..
  • @stfx:我的答案的最后一部分弄错了。并不是说 const 字段将不再可以通过元数据发现;您将无法将它们与常规字段区分开来。我已经更正了我的答案。
【解决方案2】:

增加的程序集大小基本上是由于 C# 编译器发出了关于 const-ness 的额外元数据。


你希望这个短节目有什么输出?

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 字段也发出完整的元数据是一件好事。

【讨论】:

  • 感谢您的澄清。如果它们的处理方式类似于 c++ #defines,我会更喜欢,因为它们不是真正的字段,没有被编译,并且反射没有返回它们,但我猜设计师想要反射支持;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-14
相关资源
最近更新 更多