C# 类位转换器的通用方法?
我最近遇到了一种情况,需要创建一个通用方法来从字节数组中读取数据类型 我创建了以下类:C# 类位转换器的通用方法?,c#,marshalling,unsafe,C#,Marshalling,Unsafe,我最近遇到了一种情况,需要创建一个通用方法来从字节数组中读取数据类型 我创建了以下类: public class DataStream { public int Offset { get; set; } public byte[] Data { get; set; } public T Read<T>() where T : struct { unsafe { int dataLen = M
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;
}
}
}
公共类数据流
{
公共整数偏移量{get;set;}
公共字节[]数据{get;set;}
public T Read(),其中T:struct
{
不安全的
{
int dataLen=Marshal.SizeOf(typeof(T));
IntPtr dataBlock=Marshal.AllocHGlobal(dataLen);
封送处理副本(数据、偏移量、数据块、数据长度);
T type=*((T*)dataBlock.ToPointer());
FreeHGlobal元帅(数据块);
偏移量+=数据长度;
返回类型;
}
}
}
现在,撇开取消分配问题不谈,这段代码不会使用以下消息编译:
Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')
无法获取托管类型('T')的地址、大小或声明指向该类型的指针
这看起来很奇怪,因为您应该能够基于方法上的where T:struct
约束执行上述操作
如果这段代码非常不正确,有没有简单的方法将一系列字节转换成“T
”类型
谢谢 不要试图通过指针操作来实现这一点,您应该切换代码以使用。此方法是专为此场景设计的。既然已经给出了答案,让我解释一下为什么您的原始代码不适用于您: 这看起来很奇怪,因为您应该能够基于方法上的where T:struct约束执行上述操作 不是真的。可以有指向非托管类型的原始指针。这在C语言规范(18.2)中定义如下: 与引用(引用类型的值)不同,指针不被垃圾收集器跟踪-垃圾收集器不知道指针及其指向的数据。因此,不允许指针指向引用或包含引用的结构,指针的referent类型必须是非托管类型。 非托管类型是指不是引用类型且在任何嵌套级别都不包含引用类型字段的任何类型。换句话说,非托管类型是以下类型之一:
,sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,char
,double
,或decimal
bool
- 任何枚举类型
- 任何指针类型
- 仅包含非托管类型字段的任何用户定义的结构类型
因此有很多限制,对于一般方法,
T:struct
对于任何特定的实例化都可能或可能不符合这些限制,因此像T*
这样的构造是非法的。最好有一个特殊的泛型类型参数约束来覆盖非托管类型,但就目前情况而言,CLR中没有这样的约束。我曾经写过一篇文章,解释了如何做到这一点,但比Marshal.PtrToStructure快了很多倍。代码示例使用动态代码生成将泛型类型T复制到位流中或从位流复制到位流中。我不久前写过这篇文章,也是为了做同样的事情:
不过,您必须将C++/CLI项目添加到Visual Studio解决方案中。假设:
- 顺序或明确的结构(否则是非常糟糕的想法)
- 正确的数据大小(您应该预先检查并抛出)
不安全的TStruct BytesToStructure(字节[]数据),其中TStruct:struct
{
固定(字节*数据ptr=数据)
return(TStruct)Marshal.ptr结构(新的IntPtr(dataPtr),typeof(TStruct));
}
不安全字节[]StructureToBytes(tsstruct st),其中tsstruct:struct
{
var bytes=新字节[Marshal.SizeOf(st)];
已修复(字节*ptr=bytes)封送。StructureToPtr(st,新的IntPtr(ptr),true);
返回字节;
}
感谢您的快速回复,这正是我想要的!啊,我明白了。我从未考虑过结构约束可能会允许其他类型的托管类型进入混合。谢谢你的解释!这是我见过的最好、最紧凑的解决方案。我开始使用它们,它们真的很有用。
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;
}