的GCHandle。如何固定包含定义为类的字段的对象

时间:2016-01-22 08:07:22

标签: c# class struct marshalling

我想在.Net中固定一些托管对象,将其数据复制到字节数组中。对于固定和复制,我使用下一个代码:

C c = new C();
byte[] b = new byte[Marshal.SizeOf(c)];
GCHandle gch = GCHandle.Alloc(c, GCHandleType.Pinned);
Marshal.Copy(gch.AddrOfPinnedObject(), b, 0, b.Length);
gch.Free();

当我将对象定义声明为:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct A
{
    public int a;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct C
{
    public A a0;
    public A a1;
    public A a2;
}

一切正常。当我将对象定义声明为:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct A
{
    public int a;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
class C
{
    public A a0;
    public A a1;
    public A a2;
}

所有工作都很好。但是当我将我的对象声明为 class

[StructLayout(LayoutKind.Sequential, Pack = 1)]
class A
{
    public int a;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
class C
{
    public A a0;
    public A a1;
    public A a2;
}

然后在 GCHandle.Alloc(...) 中抛出ArgumentException。对象包含非原始或非blittable数据。'

为什么当 A 被定义为struct时,一切正常。但是当班级没有工作的时候?可以使用A和C两种类型定义为类吗?

1 个答案:

答案 0 :(得分:3)

您正试图打败垃圾收集器,您正在将引用复制到类对象中,而不是GC无法看到的内存块。这是无效的,这样的参考值在短至一纳秒后变成垃圾。每当GC运行时,您都不知道何时。 GC无法更新引用,它不知道它存在。

只有当这些引用指向的对象无法进行垃圾回收且无法移动时,才能正确执行此操作。换句话说,他们必须固定。你说服了编组,你可能会认为这是正确的:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
class C
{
    public IntPtr a0;
    public IntPtr a1;
    public IntPtr a2;
}

您已经知道如何获取这些IntPtr值。使用CLR未检查的附加规定,只要其他代码可能正在使用内存blob,就会保留这些对象。