C# 为什么不能使用Marshal.Copy()更新结构?
我有一些代码打算从字节数组中获取结构:C# 为什么不能使用Marshal.Copy()更新结构?,c#,.net,marshalling,managed,C#,.net,Marshalling,Managed,我有一些代码打算从字节数组中获取结构: 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);
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;
}
public static T GetValue(byte[]data,int start),其中T:struct
{
td=默认值(T);
int elementsize=Marshal.SizeOf(typeof(T));
GCHandle sh=GCHandle.Alloc(d,GCHandleType.pinted);
Marshal.Copy(数据,开始,sh.AddrOfPinnedObject(),elementsize);
sh.Free();
返回d;
}
但是,结构d
永远不会被修改,并且总是返回其默认值
我已经找到了“正确”的方法来做这件事,并且正在使用它,但是我仍然很好奇,因为我不明白为什么上面的方法不起作用
它非常简单:分配一些内存,d,得到一个指向它的指针,将一些字节复制到它所指向的内存中,然后返回。
不仅如此,而且当我使用类似的代码,但d是T的数组时,它工作得很好。
除非sh.AddrOfPinnedObject()不是真正指向d
,但它的意义是什么
有人能告诉我为什么上述方法不起作用吗?警告实施详细信息警报,在未来的.Net版本中可能不会出现这种情况
structs
是值类型,通常存储在堆栈(*)上,而不是堆上。结构的地址没有意义,因为它们是通过值传递的,而不是通过引用传递的。struct的数组是一个引用类型,即指向堆上内存的指针,因此内存中的地址是完全有效的
addrofpindedobject
的要点是获取内存被固定的对象的地址,而不是结构
此外,Eric Lippert还写过关于引用类型和值类型的文章
(*)除非:
1它们是类上的字段2它们是盒装的
3它们是“捕获的变量”
4它们位于迭代器块中
(注意,第3点和第4点是第1点的推论)以下是一个工作示例:
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);
}
}
}
这就是你的问题开始的地方。结构是值类型,GCHandle.Alloc()只能为引用类型分配句柄。在垃圾收集堆上分配其对象的类型。以及那种使钉住变得合理的方法。C#编译器在这里有点太有用了,它会自动发出一个装箱转换来装箱值并使语句工作。这通常非常好,并且会产生一种错觉,认为值类型是从System.Object派生的。像鸭子一样的庸医打字
问题是,Marshal.Copy()将更新该值的装箱副本。不是你的变量。所以你看不到它的变化
只有使用Marshal.PtrToStructure()才能直接更新结构值。它包含将已发布的结构布局(StructLayout属性)转换为内部布局所需的smarts。这是不一样的,否则无法发现。应该注意,
结构
可以存储在堆上
,当它们是类的成员时就会发生这种情况
+1感谢您解释d的自动装箱;我现在完全可以看到我的代码是如何工作的(不是!)。值类型是从System.Object派生的。这不是幻觉!当然如果抽象永远不会泄漏就好了。只是出于好奇,什么是“正确”的方法?@Dmitry,您好,正确的方法是使用PtrToStructure()传递一个指向包含结构内容的非托管内存的指针,如下所述:使用reflector可以看到PtrToStructure()实例化一个新对象并填充它,虽然它是如何做到的,但我不确定,因为我相信这些细节在CLR中,我看不到()
[StructLayout(LayoutKind.Explicit, Size = 3)]
public struct TestStruct
{
[FieldOffset(0)]
public byte z;
[FieldOffset(1)]
public short y;
}
GCHandle sh = GCHandle.Alloc(d, GCHandleType.Pinned);