通用BitConverter式方法?

时间:2009-09-21 16:59:44

标签: 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 = 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”类型?

谢谢!

5 个答案:

答案 0 :(得分:9)

您应该切换代码以使用Mashal.PtrToStructure,而不是尝试通过指针操作来执行此操作。此方法专为此方案而设计。

答案 1 :(得分:7)

由于已经给出答案,让我解释为什么你的原始代码不适合你:

  

哪个,看起来很奇怪,因为你应该能够根据方法中的T:struct约束进行上述操作。

不是真的。您可以拥有指向非托管类型的原始指针。这在C#语言规范(18.2)中定义如下:

  

与引用(引用类型的值)不同,垃圾收集器不跟踪指针 - 垃圾收集器不知道指针及它们指向的数据。因此,不允许指针指向引用或包含引用的结构,指针的引用类型必须是非托管类型。   非托管类型是任何不是引用类型的类型,并且在任何嵌套级别都不包含引用类型字段。换句话说,非托管类型是以下之一:

     
      
  • sbytebyteshortushortintuintlongulongcharfloatdoubledecimalbool
  •   
  • 任何枚举类型
  •   
  • 任何指针类型
  •   
  • 任何用户定义的 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项目。