将托管数组转换为结构数组而不复制

时间:2016-03-07 10:22:29

标签: c# .net

我有第三方库提供的托管数组。数组的类型不直接反映存储在数组中的数据,而是将数据解释为整数。

所以int[] data = Lib.GetData();给了我一个整数数组,我希望将其转换为DataStructure数组,它看起来像这样。

struct DataStructure {
    public int Id;
    public double Value;
}

当前我使用Marshal.Copy(可以看到here的实现),但复制整个内容似乎有些过分。

这样的事情是否存在:

int[] data = Lib.GetData();
DataStructure[] dataStructs = InterpretAs<DataStructure>(data);

不需要复制,但可以像dataStruct那样访问dataStruct[1].Id元素吗?

编辑2:
如果我只想要一个DataStructure,我可以使用

public static T ToStruct<T>(byte[] bytes) where T : struct
{
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    T something = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
    handle.Free();
    return something;
}

不需要复制。

修改
可能重复的答案目前大约有7年的历史,它们包括复制数据或实施黑客攻击。

1 个答案:

答案 0 :(得分:2)

实际上有作弊,但它是丑陋 不安全 完全不安全作弊:

[StructLayout(LayoutKind.Sequential)]
//[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct DataStructure
{
    public int Id;
    public double Value;
}

[StructLayout(LayoutKind.Explicit)]
public struct DataStructureConverter
{
    [FieldOffset(0)]
    public int[] IntArray;

    [FieldOffset(0)]
    public DataStructure[] DataStructureArray;
}

然后你可以毫无问题地转换它:

var myarray = new int[8];
myarray[0] = 1;
myarray[3] = 2;
//myarray[4] = 2;

DataStructure[] ds = new DataStructureConverter { IntArray = myarray }.DataStructureArray;

int i1 = ds[0].Id;
int i2 = ds[1].Id;

请注意,根据DataStructure的大小(如果是16个字节或12个字节),您必须使用Pack = 4(如果它是12个字节)或者您不需要任何内容​​(稍后参见说明(1)

我要补充一点,这种技术没有记录,完全不安全。它甚至有一个问题:ds.Length不是DataStructure[]的长度,而是int[]的长度(所以在给定的例子中它是8,而不是2)

“技术”与我描述的here相同,最初描述为here

解释(1)

sizeof(double)是8个字节,因此Value通常在8字节边界上对齐,所以通常Id之间存在“间隙”(sizeof(int) == 4 )和4个字节的Value。通常是sizeof(DataStructure) == 16。根据DataStructure的构建方式,不会出现这种差距,因此Pack = 4强制对齐4字节边界。