![](/img/trans.png)
[英]How to determine and check whether a type in assembly is Custom type or Primitive type using reflection in .NET?
[英]Using reflection to determine how a .Net type is layed out in memory
我正在尝试在C#中优化解析器组合器。 当序列化格式与内存中格式匹配时,一种可能的优化方法是对一个实例甚至该类型的多个实例进行(不安全)内存解析。
我想编写确定内存中格式是否与序列化格式匹配的代码,以便动态确定是否可以应用优化。 (显然,这是一个不安全的优化,由于种种微妙的原因,它可能无法正常工作。我只是在做实验,而不打算在生产代码中使用它。)
我使用属性[StructLayout(LayoutKind.Sequential,Pack = 1)]强制不填充,并强制内存中的顺序与声明顺序匹配。 我通过反射检查了该属性,但实际上所有这些确认都是“无填充”。 我还需要字段的顺序。 (我强烈希望不必为每个字段手动指定FieldOffset属性,因为这很容易出错。)
我以为我可以使用GetFields返回的字段顺序,但是文档明确指出该顺序未指定。
鉴于我要使用StructLayout属性强制字段的顺序,是否有办法反映这种顺序?
编辑我可以限制所有字段必须是blittable的限制 。
如果将LayoutKind.Sequential
与blittable类型一起使用,则不必要
您不需要使用反射或任何其他机制来查找结构字段在内存中的顺序,只要所有字段都是可蓝调的即可。
对于声明的结构的blittable领域LayoutKind.Sequential
将在其中字段声明的顺序存储。 这就是LayoutKind.Sequential
意思!
从本文档中 :
对于blittable类型,LayoutKind.Sequential控制托管内存中的布局和非托管内存中的布局。 对于不可拆分的类型,当类或结构封送为非托管代码时,它控制布局,但不控制托管内存中的布局。
请注意,这不会告诉您每个字段使用了多少填充。 要找出答案,请参阅下文。
在使用LayoutKind.Auto
时确定字段顺序,或者在使用任何布局时确定字段偏移量
如果您愿意使用不安全的代码并且不使用反射,则很容易找到struct字段的偏移量。
您只需要获取结构每个字段的地址,并计算从结构开始处的偏移量即可。 了解每个字段的偏移量后,您可以计算它们的顺序(以及它们之间的任何填充字节)。 要计算用于最后一个字段(如果有的话)的填充字节,您还需要使用sizeof(StructType)
获得结构的总大小。
以下示例适用于32位和64位。 请注意,您不需要使用fixed
关键字,因为该结构由于已在堆栈中而已被固定(如果尝试将其用于fixed
,则会出现编译错误):
using System;
using System.Runtime.InteropServices;
namespace Demo
{
[StructLayout(LayoutKind.Auto, Pack = 1)]
public struct TestStruct
{
public int I;
public double D;
public short S;
public byte B;
public long L;
}
class Program
{
void run()
{
var t = new TestStruct();
unsafe
{
IntPtr p = new IntPtr(&t);
IntPtr pI = new IntPtr(&t.I);
IntPtr pD = new IntPtr(&t.D);
IntPtr pS = new IntPtr(&t.S);
IntPtr pB = new IntPtr(&t.B);
IntPtr pL = new IntPtr(&t.L);
Console.WriteLine("I offset = " + ptrDiff(p, pI));
Console.WriteLine("D offset = " + ptrDiff(p, pD));
Console.WriteLine("S offset = " + ptrDiff(p, pS));
Console.WriteLine("B offset = " + ptrDiff(p, pB));
Console.WriteLine("L offset = " + ptrDiff(p, pL));
Console.WriteLine("Total struct size = " + sizeof(TestStruct));
}
}
long ptrDiff(IntPtr p1, IntPtr p2)
{
return p2.ToInt64() - p1.ToInt64();
}
static void Main()
{
new Program().run();
}
}
}
在使用LayoutKind.Sequential
时确定字段偏移量
如果你的结构使用LayoutKind.Sequential
那么你可以使用Marshal.OffsetOf()
来获得直接补偿,但这并不工作, LayoutKind.Auto
:
foreach (var field in typeof(TestStruct).GetFields())
{
var offset = Marshal.OffsetOf(typeof (TestStruct), field.Name);
Console.WriteLine("Offset of " + field.Name + " = " + offset);
}
如果您使用的是LayoutKind.Sequential
,则这显然是一种更好的方法,因为它不需要unsafe
代码,并且代码要短得多,并且您不需要事先知道字段的名称。 就像我在上面说的那样,不需要确定字段在内存中的顺序-但是如果您需要了解使用了多少填充,这可能会很有用。
为那些想知道顺序和布局类型的人提供参考。 例如,如果一个类型包含不可引用的类型。
var fields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
fields.SortByFieldOffset();
var isExplicit = typeof(T).IsExplicitLayout;
var isSequential = typeof(T).IsLayoutSequential;
它使用了我编写的扩展方法:
public static void SortByFieldOffset(this FieldInfo[] fields) {
Array.Sort(fields, (a, b) => OffsetOf(a).CompareTo(OffsetOf(b)) );
}
private static int OffsetOf(FieldInfo field) {
return Marshal.OffsetOf(field.DeclaringType, field.Name).ToInt32();
}
MSDN包含有关IsLayoutSequential的有用信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.