我正在尝试将静态初始值设定项从一个 DLL 复制到另一个。
如果你在 C# 中有一个静态数组初始值设定项,你会得到这样的东西:
.class private auto ansi <PrivateImplementationDetails>{0D3E8B0E-F218-435F-989D-9D04F550A786}
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
.field assembly static valuetype <PrivateImplementationDetails>{0D3E8B0E-F218-435F-989D-9D04F550A786}/__StaticArrayInitTypeSize=120 $$method0x6000060-1 = ((binary data))
我发现读取此数据的最简单方法是使用此方法:
int size = field.FieldType.StructLayoutAttribute.Size;
byte[] data = new byte[size];
RuntimeHelpers.InitializeArray(data, field.FieldHandle);
基本上这将为您提供上面提到的“二进制数据”。
问题 1:Dictionary
会怎样?这仍然有效吗?我无法弄清楚这里到底发生了什么(它似乎被反编译隐藏了)...
我已经发现在实现细节中使用的 GUID 是 ModuleBuilder
中的版本 ID。使用其他显式字段信息,这意味着您应该能够复制数据。
问题 2:如何使用 Reflection.Emit 在另一个 ModuleBuilder
/FieldBuilder
中写回数据?
--
版本? .NET 4.5(VS2013 默认)
似乎对弹出的Dictionary
感到困惑。我在我的代码中做了一些挖掘,它们似乎作为 [string->int] 字典弹出,用于解析 switch/case 语句。
例如,看到它们可以在 mscorlib.dll v4.0.30319.18444 中找到。在 Reflector 中,它们看起来像这样:
.field assembly static class System.Collections.Generic.Dictionary`2<string, int32> $$method0x6003a20-1
至于为什么:我在对它们执行一些测试之前更改了 DLL。具有讽刺意味的是,我想要这个的原因是因为我不想弄乱实现细节 :-) 并且因为多个实例可能会带来麻烦。
换句话说,如果它们有像这样的奇特数据,我基本上只是想将它们全部复制“就好像它们是二进制 blob”,而不管类型等,同时保留名称。由于在不同的 DLL 中以相同的方式处理 IL 和数据,因此无论使用何种编译器恕我直言,这应该总是可行的,对吧?
最佳答案
经过几个小时的摆弄,这似乎是它的工作方式:
... 对于 <PrivateImplementationDetails>
中的每个字段:
- 使用值类型或引用类型。
- 值类型可以携带数据,引用类型不能。引用类型在使用前进行初始化(作为 volatile 静态变量,f.ex. 在与使用它们相同的范围内)。
从值类型 ( field.FieldType.IsValueType
) 中获取数据可以使用 RuntimeHelpers
来完成调用:
int size = GetManagedSize(field.FieldType);
byte[] data = new byte[size];
RuntimeHelpers.InitializeArray(data, field.FieldHandle);
FieldBuilder mappedField = myType.DefineInitializedData(
field.Name, data, field.Attributes);
与:
public static int GetManagedSize(Type type)
{
var method = new DynamicMethod("GetManagedSizeImpl", typeof(uint), new Type[0],
GetType().Assembly, false);
ILGenerator gen = method.GetILGenerator();
gen.Emit(OpCodes.Sizeof, type);
gen.Emit(OpCodes.Ret);
var func = (Func<uint>)method.CreateDelegate(typeof(Func<uint>));
return checked((int)func());
}
如果它是一个引用类型,你不需要做任何事情,因为它们在使用时被初始化为 volatile statics。
在此之后,您需要确保使用生成的字段信息而不是原始字段;它们将包含相同的数据。至于引用,这些被初始化为 null
.
显然,您不想弄乱实现细节本身或它们的使用方式...
关于c# - 复制/反射。发射静态数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30303834/