简体   繁体   English

为什么我不能使用Marshal.Copy()来更新结构?

[英]Why can I not use Marshal.Copy() to update a struct?

I have some code intended to get a struct from a byte array: 我有一些代码用于从字节数组中获取结构:

    public static T GetValue<T>(byte[] data, int start) where T : struct
    {
        T d = default(T);
        int elementsize = Marshal.SizeOf(typeof(T));

        GCHandle sh = GCHandle.Alloc(d, GCHandleType.Pinned);
        Marshal.Copy(data, start, sh.AddrOfPinnedObject(), elementsize);
        sh.Free();

        return d;
    }

However, the structure d is never modified, and always returns its default value. 但是,结构d永远不会被修改,并始终返回其默认值。

I have looked up the 'correct' way to do this and am using that instead, but am still curious, as I cannot see why the above should not work. 我已经查找了“正确”的方法,并且正在使用它,但仍然很好奇,因为我不明白为什么上面的内容不起作用。

Its as simple as can be: allocate some memory, d, get a pointer to it, copy some bytes into the memory pointed at by this, return. 它可以很简单:分配一些内存,d,获取指向它的指针,将一些字节复制到由此指向的内存中,返回。 Not only that, but when I use similar code but with d being an array of T, it works fine. 不仅如此, 但是当我使用类似的代码但是d是T的数组时,它工作正常。 Unless sh.AddrOfPinnedObject() isn't really pointing to d , but then what is the point of it? 除非sh.AddrOfPinnedObject()没有真正指向d ,但那有什么意义呢?

Can anyone tell me why the above does not work? 谁能告诉我为什么以上不起作用?

    GCHandle sh = GCHandle.Alloc(d, GCHandleType.Pinned);

That's where your problem started. 这就是你的问题开始的地方。 A struct is a value type , GCHandle.Alloc() can only allocate handles for reference types . struct是一种值类型 ,GCHandle.Alloc()只能为引用类型分配句柄。 The kind whose objects are allocated on the garbage collected heap. 在垃圾收集堆上分配对象的类型。 And the kind that make pinning sensible. 而那种使钉扎变得明智的东西。 The C# compiler is being a bit too helpful here, it automatically emits a boxing conversion to box the value and make the statement work. C#编译器在这里有点太有用了,它会自动发出一个装箱转换来装箱值并使语句有效。 Which is normally very nice and creates the illusion that value types are derived from System.Object. 这通常非常好,并且产生了从System.Object派生值类型的错觉。 Quacks-like-a-duck typing. 嘎嘎叫鸭子打字。

Problem is, Marshal.Copy() will update the boxed copy of the value. 问题是,Marshal.Copy()将更新值的盒装副本 Not your variable. 不是你的变量。 So you don't see it change. 所以你没有看到它改变。

Directly updating the structure value is only possible with Marshal.PtrToStructure(). 只有Marshal.PtrToStructure()才能直接更新结构值。 It contains the required smarts to convert the published layout of the struct (StructLayout attribute) to the internal layout. 它包含将结构的已发布布局(StructLayout属性)转换为内部布局所需的智能。 Which is not the same and otherwise undiscoverable. 这是不一样的,否则是不可发现的。

Warning implementation detail alert, this may not be true in future versions of .Net. 警告实施细节警报,在.Net的未来版本中可能不是这样。

structs are value types and are (generally) stored on the stack (*) , not on the heap. structs是值类型,并且(通常)存储在堆栈(*)上,而不是堆上。 An address of a struct is meaningless since they are passed by value, not by reference. 结构的地址没有意义,因为它们是按值传递的,而不是通过引用传递的。 The array of struct is a reference type, that is a pointer to memory on the heap, so the address in memory is perfectly valid. struct数组是一个引用类型,它是指向堆上内存的指针,因此内存中的地址完全有效。

The point of AddrOfPinnedObject is to get the address of an object thats memory is pinned, not a struct . AddrOfPinnedObject是获取内存固定的对象的地址,而不是结构

Additionally, Eric Lippert has written a series of very good blog posts on the subject of reference types and value types. 此外,Eric Lippert撰写了一系列关于参考类型和价值类型的非常好的博客文章

(*) Unless: (*)除非:

1 They are fields on a class 1他们是班上的字段
2 They are boxed 2他们是盒装的
3 They are "captured variables" 3它们是“捕获的变量”
4 They are in an iterator block 4它们位于迭代器块中

(nb points 3 and 4 are corollaries of point 1) (nb点3和4是第1点的推论)

Here's a working example: 这是一个有效的例子:

public static T GetValue<T>(byte[] data, int start) where T : struct
{
    int elementsize = Marshal.SizeOf(typeof(T));

    IntPtr ptr = IntPtr.Zero;

    try
    {
        ptr = Marshal.AllocHGlobal(elementsize);

        Marshal.Copy(data, start, ptr, elementsize);
        return (T)Marshal.PtrToStructure(ptr, typeof(T));
    }
    finally
    {
        if (ptr != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(ptr);
        }
    }
}

But I would use explicit layout here because of struct alignment . 但是我会在这里使用显式布局,因为结构对齐

[StructLayout(LayoutKind.Explicit, Size = 3)]
public struct TestStruct
{
    [FieldOffset(0)]
    public byte z;

    [FieldOffset(1)]
    public short y;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM