5

我最近遇到了一种情况,我需要创建一个通用方法来从字节数组中读取数据类型。

我创建了以下类:


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;
        }
    }
}

现在,抛开取消分配问题不谈,此代码不会与此消息一起编译:

无法获取托管类型 ('T') 的地址、大小或声明指向托管类型的指针

这似乎很奇怪,因为您应该能够根据对where T : struct方法的约束来执行上述操作。

如果这段代码非常不正确,是否有任何简单的方法可以获取一系列字节并将它们转换为 ' T' 类型?

谢谢!

4

5 回答 5

9

您应该将代码切换为使用Mashal.PtrToStructure ,而不是尝试通过指针操作来执行此操作。此方法是专门为这种情况设计的。

于 2009-09-21T17:05:30.643 回答
8

既然已经给出了答案,让我解释一下为什么您的原始代码不适合您:

这看起来很奇怪,因为您应该能够基于 where T : struct 方法的约束来执行上述操作。

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

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

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, 或bool.
  • 任何枚举类型
  • 任何指针类型
  • 任何仅包含非托管类型字段的用户定义结构类型。

所以有很多限制,对于泛型方法,T:struct对于任何特定的实例化可能会或可能不会符合它们,因此构造 likeT*是非法的。有一个特殊的泛型类型参数约束来覆盖非托管类型会很好,但就目前而言,CLR 中没有。

于 2009-09-21T17:16:45.193 回答
2

有一次,我写了这篇文章来解释如何做到这一点,但比 Marshal.PtrToStructure 快很多倍。代码示例使用动态代码生成将泛型类型 T 复制到比特流/从比特流复制。

于 2009-09-25T16:13:54.647 回答
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;
}
于 2012-10-24T09:35:38.527 回答
0

我写了这个一段时间回来做同样的事情: http: //www.codeproject.com/Articles/33713/Generic-BinaryReader-and-BinaryWriter-Extensions

不过,您必须将 C++/CLI 项目添加到您的 Visual Studio 解决方案中。

于 2012-06-05T03:10:47.993 回答