【问题标题】:Casting a managed array to an array of structs without copying将托管数组转换为结构数组而不复制
【发布时间】:2016-03-07 10:22:29
【问题描述】:

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

所以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[1].Id 一样访问dataStruct 元素?

编辑 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 年的历史,它们包括复制数据或实施黑客攻击。

【问题讨论】:

  • 这样的构造即使在 C 中也是 UB(其中原始副本也取决于实现)。您可以想象在具有强大类型系统的托管世界中......
  • @kasperhj 你确定它会导致性能问题吗?你真的需要make it faster吗?也许您可以要求第三方从他们的方法中提供更强类型的数据?在最坏的情况下,您可以尝试unsafe 代码(它应该允许这样做),但在这样做之前您应该考虑两次。
  • “我的应用程序没有性能问题,...” - 然后不要解决你没有的问题。在对 GetData() 进行看似简单的调用之后,转换可能只是杯水车薪。
  • @kasperhj Marshal.PtrToStructure不需要复制的地方。 False... Marshal.PtrToStructure 对结构进行复制。该结构可以是“blittable”(因此 1:1 副本就足够了)或者可能需要一些编组(转换)。

标签: c# .net


【解决方案1】:

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

[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 相同。

说明(一)

sizeof(double) 是 8 个字节,所以 Value 通常在 8 个字节边界上对齐,所以通常在 Id(具有 sizeof(int) == 4)和 Value 之间存在 4 个字节的“间隙” .所以通常sizeof(DataStructure) == 16。根据 DataStructure 的构建方式,不可能有这个间隙,所以 Pack = 4 强制在 4 字节边界上对齐。

【讨论】:

  • 虽然我真的不愿意在我的生产代码中实现这一点,但它似乎确实可以解决问题。由于似乎没有其他方法可以获得相同的结果,因此这的答案。谢谢。
  • @kasperhj 为此,有一个解决方案......(即使你想使用反射也应该应用这个解决方案)在程序开始时进行一些“健全性检查”:测试该结构的副本可以正常工作。这样就不会有任何“隐藏问题”。就像在开始时执行的单元测试一样。
猜你喜欢
  • 2011-09-14
  • 1970-01-01
  • 1970-01-01
  • 2015-05-30
  • 1970-01-01
  • 2015-10-26
  • 2015-06-22
  • 2018-02-26
  • 1970-01-01
相关资源
最近更新 更多