我编写了一个工具,生成一个C#源文件,其中包含处理器密集型计算的结果(预计算结果会使我的应用程序的启动时间大约加快15分钟).它byte[]看起来像这样:
public static byte[] precomputedData = new byte[]{
0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,
0x0F,0x10, .... continue for 8000 more lines....
Run Code Online (Sandbox Code Playgroud)
我想知道,是否存在将代码硬编码到代码而不是将其写入二进制文件的隐藏成本?具体来说,我担心C#可能会在RAM中存储两份数据; 一个副本包含要构建的所有指令/代码precomputedData(用于反射目的)和另一个包含构建实际结果的副本precomputedData.这准确吗?或者是硬编码或将其存储在二进制文件中之间的选择,纯粹的偏好?
我编写了一个小测试程序并查看了 IL 代码。对于静态字段,会生成静态构造函数来初始化该字段。对于数组,Bart De Smet 撰写的关于C# 数组初始值设定项如何工作的博客文章中有一个非常好的解释。
.method private hidebysig specialname rtspecialname static
void .cctor () cil managed
{
// Method begins at RVA 0x2b50
// Code size 24 (0x18)
.maxstack 8
IL_0000: ldc.i4.s 10
IL_0002: newarr [mscorlib]System.Int32
IL_0007: dup
IL_0008: ldtoken field valuetype
'<PrivateImplementationDetails>{0BB3CD0B-D585-49BF-8408-2CCB3FA63A32}'/'__StaticArrayInitTypeSize=40'
'<PrivateImplementationDetails>{0BB3CD0B-D585-49BF-8408-2CCB3FA63A32}'::'$$method0x6000013-1'
IL_000d: call void
[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class
[mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle)
IL_0012: stsfld int32[] Hgx.Test.Program::myarray
IL_0017: ret
} // end of method Program::.cctor
Run Code Online (Sandbox Code Playgroud)
该<PrivateImplementationDetails>...$$method字段定义为
.field assembly static valuetype
'<PrivateImplementationDetails>{0BB3CD0B-D585-49BF-8408-2CCB3FA63A32}'/'__StaticArrayInitTypeSize=40'
'$$method0x6000013-1' at I_00002b28
Run Code Online (Sandbox Code Playgroud)
据我所知,根据我之前提到的帖子,这意味着数据是直接从 PE 映像初始化的。
这是否是一个好的做法是另一个问题。
像您一样对其进行硬编码的优点是数据非常容易加载,并且很难操作数据,因为它嵌入在可以签名的文件中。当然,缺点是预计算数据的任何更改都意味着您必须重新编译项目。
我可能会使用二进制文件来存储数据,除非我真的担心有人恶意更改预先计算的数据。加载仅比将其作为静态字段稍微复杂一些,我很看重无需重新编译即可更改数据的能力。