当前位置: 首页 > 编程语言 > C#

使用反射确定.Net类型在内存中的布局Share

时间:2023-04-11 11:45:19 C#

Usingreflectiontodeterminehow.Nettypesarelayoutinmemory我正在尝试优化C#中的解析器组合器。当序列化格式与内存格式匹配时,一种可能的优化是只对要在实例上解析的数据进行(不安全的)memcpy,甚至是该类型的许多实例。我想编写代码来确定内存格式是否与序列化格式匹配,以便我可以动态确定是否可以应用优化。(显然,这是一个不安全的优化,由于各种微妙的原因可能无法正常工作。我只是在试验,不打算在生产代码中使用它。)我使用属性[StructLayout(LayoutKind.Sequential,Pack=1)]强制不填充并强制内存顺序与声明顺序匹配。我用反射检查了属性,但实际上它们都确认“没有填充”。我还需要字段的顺序。(我非常希望不必为每个字段手动指定FieldOffset属性,因为这很容易出错。)我假设我可以只使用GetFields返回的字段的顺序,但文档清楚地说明了顺序未指定。鉴于我使用StructLayout属性强制执行字段的顺序,有没有办法反映该顺序?编辑我同意所有字段都必须是blittable的限制。如果您将LayoutKind.Sequential与blittable类型一起使用,则不需要执行此操作,只要所有字段都是blittable,就不需要使用反射或任何其他机制来查找内存中结构字段的顺序。使用LayoutKind.Sequential声明的结构的blittable字段将按照字段声明的顺序存储在内存中。这就是LayoutKind.Sequential的意思!从此文档中:对于blittable类型,LayoutKind.Sequential控制托管内存中的布局和非托管内存中的布局。对于非blittable类型,它在将类或结构封送到非托管代码时控制布局,但不在托管内存中。请注意,这不会告诉您每个字段使用了多少填充。要找到它,请参阅下文。使用LayoutKind.Auto时确定字段顺序,或使用任何布局时确定字段偏移您只需要获取结构的每个字段的地址并计算其从结构开头的偏移量。知道每个字段的偏移量,您可以计算它们的顺序(以及它们之间的任何填充字节)。要计算用于最后一个字段(如果有)的填充字节,您还需要使用sizeof(StructType)来获取结构的总大小。以下示例适用于32位和64位。请注意,您不需要使用fixed关键字,因为该结构已经固定在堆栈中(如果您尝试使用fixed则会出现编译错误):使用System.Runtime.InteropServices;命名空间演示{[StructLayout(LayoutKind.Auto,Pack=1)]publicstructTestStruct{publicintI;公共双D;公共空头S;公共字节B;公长L;}classProgram{voidrun(){vart=newTestStruct();不安全{IntPtrp=newIntPtr(&t);IntPtrpI=newIntPtr(&t.I);IntPtrpD=newIntPtr(&t.D);IntPtrpS=newIntPtr(&t.S);IntPtrpB=newIntPtr(&t.B);IntPtrpL=newIntPtr(&t.L);Console.WriteLine("Ioffset="+ptrDiff(p,pI));Console.WriteLine("Doffset="+ptrDiff(p,pD));Console.WriteLine("S偏移="+ptrDiff(p,pS));Console.WriteLine("Boffset="+ptrDiff(p,pB));Console.WriteLine("Loffset="+ptrDiff(p,pL));Console.WriteLine("总结构大小="+sizeof(TestStruct));}}longptrDiff(IntPtrp1,IntPtrp2){returnp2.ToInt64()-p1.ToInt64();}静态语音dMain(){新程序().run();使用LayoutKind.Sequential时确定字段偏移量如果您的结构使用LayoutKind.Sequential,那么您可以使用Marshal.OffsetOf()直接获取偏移量,但这不适用于LayoutKind.Auto:foreach(varfieldintypeof(TestStruct).GetFields()){varoffset=Marshal.OffsetOf(typeof(TestStruct),field.Name);Console.WriteLine("Offsetof"+field.Name+"="+offset);如果您使用LayoutKind.Sequential,这显然是一种更好的方法,因为它不需要不安全的代码而且它更短——而且您不需要像上面那样提前知道字段的名称如上所述,没有必要确定字段在内存中的顺序-但如果您需要知道使用了多少填充,这将很有用。作为想了解订单和布局类型的人的参考。例如,如果类型包含非blittable类型。varfields=typeof(T).GetFields(BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance);fields.SortByFieldOffset();varisExplicit=typeof(T).IsExplicitLayout;使用我写的扩展方法:}privatestaticintOffsetOf(FieldInfofield){returnMarshal.OffsetOf(field.DeclaringType,field.Name).ToInt32();MSDN有关于IsLayoutSequential的有用信息。以上就是C#学习教程:利用反射判断.Net类型在内存中的布局。如果对你有用,需要了解更多C#学习教程,希望大家多加关注——本文来自网络合集,不代表立场,如涉及侵权,请右击联系管理员删除。如需转载请注明出处: