我最近遇到一个情况,我需要创建一个通用的方法来读取出数据类型字节数组。
我创建了以下类:
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
”型?
谢谢!
不要试图通过指针操作要做到这一点,你应该切换您的代码中使用Mashal.PtrToStructure 。 这种方法是专门为这种情况设计的。
因为答案已经给出,让我来解释一下为什么你原来的代码没有为你工作:
其中,似乎有些奇怪,因为你应该能基础上,其中T做上述操作:结构约束的方法。
并不是的。 你可以有非托管类型的原始指针。 这被定义为在C#语言规范(18.2)如下:
不同于参考文献(引用类型的值),指针不会被垃圾收集器跟踪 - 垃圾收集器没有指针和它们所指向的数据的知识。 由于这个原因,指针不能指向一个参考或包含引用一个结构,和一个指针所指示的对象类型必须是一个非托管型 。 非托管型是任何类型不引用类型和在任何嵌套级别不包含引用类型字段。 换言之, 非托管型是下列之一:
-
sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, decimal
,或bool
。 - 任何枚举类型 。
- 任何指针型 。
- 任何用户定义的结构类型仅包含非托管类型的字段。
因此,有相当多的限制,和通用的方法, T:struct
可能会或可能不符合他们对于任何特定的实例,所以构建类似T*
是非法的。 这将是很好,有一个特殊的泛型类型参数的约束来覆盖非托管类型,但因为它的立场,没有一个在CLR。
有一次,我写了这篇文章 ,说明如何做到这些,但很多时候比Marshal.PtrToStructure更快。 该代码示例使用动态代码生成与通用类型T从比特流拷贝到/。
假设:
- 连续的或显式结构(否则很糟糕的主意)
- 正确的数据大小(你应该预先检查和投掷)
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;
}
我写这一段时间回做同样的事情: http://www.codeproject.com/Articles/33713/Generic-BinaryReader-and-BinaryWriter-Extensions
你必须到C ++ / CLI项目添加到您的Visual Studio解决方案虽然。