澄清 IL 生成的引用字符串的代码

我今天正在执行一些重构,我注意到一件我无法理解的奇怪事情......或者更好的是,我部分同意我在网上找到的内容,但仍有一些问题。


请考虑这个简单的例子


 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::a

IL_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 8


IL_0000: ldarg.0      // this

IL_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::a

IL_000a: ret          


 } // end of method Program::.cctor

  } // end of class ConsoleApp4.Program

对于静态字符串,它的行为符合我的预期。而不是 const 它在堆栈上加载了一个新值......事实上,它在这里查看 ldstr 操作码


将新对象引用推送到存储在元数据中的字符串文字


我在这里读过


现在,无论在代码中何处引用 myInt,MSIL 都无需执行“ldloc.0”来从变量中获取值,而是将硬编码到 MSIL 中的常量值加载。因此,使用常量通常具有较小的性能和内存优势。但是,为了使用它们,您必须在编译时获得变量的值,以及在编译时对该常量的任何引用,即使它们在一个不同的组件,将进行此替换。


如果您知道编译时的值,常量肯定是一个有用的工具。如果你不这样做,但想确保你的变量只设置一次,你可以使用 C# 中的 readonly 关键字(在 MSIL 中映射为 initonly)来指示变量的值只能在构造函数中设置;之后,更改它是错误的。这通常在字段有助于确定类的身份时使用,并且通常设置为等于构造函数参数。


但是我为什么要体验更好的性能呢?(即使考虑到它是相当可追溯的)?内存占用呢?


慕勒3428872
浏览 152回答 1
1回答

茅侃侃

考虑这段代码: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)。
打开App,查看更多内容
随时随地看视频慕课网APP