我今天正在执行一些重构,我注意到一件我无法理解的奇怪事情......或者更好的是,我部分同意我在网上找到的内容,但仍有一些问题。请考虑这个简单的例子 class Program{ public static readonly string a = "a"; public const string b = "b"; static void Main(string[] args) { Console.WriteLine(a); Console.WriteLine(b); }}现在,如果我查看生成的 IL 代码(通过来自 resharp 的 IL 浏览器获得)我看到以下代码.method private hidebysig static void Main( string[] args) cil managed { .entrypoint.maxstack 8// [16 13 - 16 34]IL_0000: ldsfld string ConsoleApp4.Program::aIL_0005: call void [mscorlib]System.Console::WriteLine(string)// [18 13 - 18 34]IL_000a: ldstr "b"IL_000f: call void [mscorlib]System.Console::WriteLine(string)// [19 9 - 19 10]IL_0014: ret } // end of method Program::Main .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { .maxstack 8IL_0000: ldarg.0 // thisIL_0001: call instance void [mscorlib]System.Object::.ctor()IL_0006: ret } // end of method Program::.ctor .method private hidebysig static specialname rtspecialname void .cctor() cil managed {.maxstack 8// [11 9 - 11 47]IL_0000: ldstr "a"IL_0005: stsfld string ConsoleApp4.Program::aIL_000a: ret } // end of method Program::.cctor } // end of class ConsoleApp4.Program对于静态字符串,它的行为符合我的预期。而不是 const 它在堆栈上加载了一个新值......事实上,它在这里查看 ldstr 操作码将新对象引用推送到存储在元数据中的字符串文字我在这里读过现在,无论在代码中何处引用 myInt,MSIL 都无需执行“ldloc.0”来从变量中获取值,而是将硬编码到 MSIL 中的常量值加载。因此,使用常量通常具有较小的性能和内存优势。但是,为了使用它们,您必须在编译时获得变量的值,以及在编译时对该常量的任何引用,即使它们在一个不同的组件,将进行此替换。如果您知道编译时的值,常量肯定是一个有用的工具。如果你不这样做,但想确保你的变量只设置一次,你可以使用 C# 中的 readonly 关键字(在 MSIL 中映射为 initonly)来指示变量的值只能在构造函数中设置;之后,更改它是错误的。这通常在字段有助于确定类的身份时使用,并且通常设置为等于构造函数参数。但是我为什么要体验更好的性能呢?(即使考虑到它是相当可追溯的)?内存占用呢?
1 回答
茅侃侃
TA贡献1842条经验 获得超22个赞
考虑这段代码:
public class Program
{
public const int ConstField1 = 1;
public const int ConstField2 = 2;
public const int ConstField3 = 3;
public const int ConstField4 = 4;
}
这四个 const int32 数字仅存储在与程序集元数据对应的内存中(因此可以通过反射获得),而不会存储在实际的运行时类型信息中。与 相比static readonly,这节省了 16 个字节的内存。在字符串的情况下,运行时也不必在实际用于其他代码之前分配字符串(因为ldstr不用于初始化字段)。您可能会争辩说这并没有节省多少,但考虑一下枚举 - 它们基本上是具有大量 const 字段的静态类型。
性能改进也很明显——因为不需要每次使用时都获取值,内存碎片减少了,并且可以对否则不可能的值执行其他优化(例如简化表达式,如BindingFlags.NonPublic | BindingFlags.Instance) . 此外,不需要调用静态构造函数,因此这是另一点(尽管在某些情况下可能不会调用它,请参阅 参考资料beforefieldinit)。
- 1 回答
- 0 关注
- 151 浏览
添加回答
举报
0/150
提交
取消
