我最近遇到过这种情况,我需要创建一个通用方法来从字节数组中读取数据类型。
我创建了以下类:
public class DataStream
{
public int Offset { get; set; }
public byte[] Data { get; set; }
public T Read<T>() where T : struct
{
unsafe
{
int dataLen = Marshal.SizeOf( typeof( T ) );
IntPtr dataBlock = Marshal.AllocHGlobal( dataLen );
Marshal.Copy( Data, Offset, dataBlock, dataLen );
T type = *( ( T* )dataBlock.ToPointer() );
Marshal.FreeHGlobal( dataBlock );
Offset += dataLen;
return type;
}
}
}
现在,除了解除分配问题,此代码无法使用此消息进行编译:
Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')
这看起来很奇怪,因为您应该能够根据方法的where T : struct
约束来执行上述操作。
如果此代码非常不正确,是否有任何简单的方法可以获取一系列字节并将其转换为“T
”类型?
谢谢!
答案 0 :(得分:9)
您应该切换代码以使用Mashal.PtrToStructure,而不是尝试通过指针操作来执行此操作。此方法专为此方案而设计。
答案 1 :(得分:7)
由于已经给出答案,让我解释为什么你的原始代码不适合你:
哪个,看起来很奇怪,因为你应该能够根据方法中的T:struct约束进行上述操作。
不是真的。您可以拥有指向非托管类型的原始指针。这在C#语言规范(18.2)中定义如下:
与引用(引用类型的值)不同,垃圾收集器不跟踪指针 - 垃圾收集器不知道指针及它们指向的数据。因此,不允许指针指向引用或包含引用的结构,指针的引用类型必须是非托管类型。 非托管类型是任何不是引用类型的类型,并且在任何嵌套级别都不包含引用类型字段。换句话说,非托管类型是以下之一:
sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,char
,float
,double
,decimal
或bool
。- 任何枚举类型。
- 任何指针类型。
- 任何用户定义的 struct-type ,其中仅包含非托管类型的字段。
因此存在相当多的限制,对于通用方法,T:struct
可能会或可能不符合任何特定实例化它们,因此像T*
这样的构造是非法的。有一个特殊的泛型类型参数约束来覆盖非托管类型会很好,但就目前而言,CLR中没有一个。
答案 2 :(得分:2)
有一次,我写了this article解释如何做到这一点,但比Marshal.PtrToStructure快许多倍。代码示例使用动态代码生成将通用类型T复制到比特流中。
答案 3 :(得分:1)
假设:
unsafe TStruct BytesToStructure<TStruct>(byte[] data) where TStruct : struct
{
fixed (byte* dataPtr = data)
return (TStruct)Marshal.PtrToStructure(new IntPtr(dataPtr), typeof(TStruct));
}
unsafe byte[] StructureToBytes<TStruct>(TStruct st) where TStruct : struct
{
var bytes = new byte[Marshal.SizeOf(st)];
fixed (byte* ptr = bytes) Marshal.StructureToPtr(st, new IntPtr(ptr), true);
return bytes;
}
答案 4 :(得分:0)
我写了一段时间回来做同样的事情:http://www.codeproject.com/Articles/33713/Generic-BinaryReader-and-BinaryWriter-Extensions
您必须在Visual Studio解决方案中添加C ++ / CLI项目。