2

我在内存中有一个二进制序列化对象,我想通过在 C# 中使用指针(不安全代码)从内存中读取它。请查看以下从内存流中读取的函数。

static Results ReadUsingPointers(byte[] data)
{
    unsafe
    {
        fixed (byte* packet = &data[0])
        {
            return *(Results*)packet;
        }
    }
}

在此return *(Results*)packet;语句中,我得到一个编译时异常“无法获取地址、获取大小或声明指向托管类型结果的指针”

这是我的结构

public struct Results
{
    public int Id;
    public int Score;
    public char[] Product;
}

根据我的理解,我的结构的所有属性都是 blittable 属性,那么为什么我会收到这个错误,如果我需要在我的结构中使用 char[] 该怎么办?

EDIT-1 让我进一步解释(请注意,这些对象是模拟的)......

背景:我有一个Results对象数组,我使用二进制序列化对它们进行了序列化。现在,在我程序的后期阶段,我需要尽快反序列化内存中的数据,因为数据量非常大。所以我在尝试,不安全的代码如何帮助我。

可以说,如果我的结构不包含public char[] Product;,我会以相当好的速度取回我的数据。但是使用 char[] 它给了我错误(编译器应该这样做)。我正在寻找在这种情况下与 char[] 一起使用的解决方案。

4

2 回答 2

2

MSDN 说

以下任何类型都可以是指针类型:

  • sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal 或 bool。

  • 任何枚举类型。

  • 任何指针类型。

  • 任何仅包含非托管类型字段的用户定义结构类型。

因此,您可以按如下方式定义结构来修复编译器错误:

public struct Results
{
    public int Id;
    public int Score;
    // Don't actually do this though.
    public unsafe char* Product;
}

这样,您可以指向数组的第一个元素。

但是,根据您编辑的问题,您需要一种不同的方法。

我有一个结果对象数组,我使用二进制序列化对它们进行了序列化。现在,在我程序的后期阶段,我需要尽快反序列化内存中的数据

通常你会BinaryFormatter为此目的使用。如果这太慢了,那么问题应该是是否可以首先避免序列化。

于 2014-04-21T08:04:35.243 回答
1

你不能指望它会起作用。

public struct Results
{
    public int Id;
    public int Score;
    public char[] Product;
}

char[]数组Product是托管类型。您的代码尝试使用 type Results*。那是一个指针类型。该文档指出,您可以声明指向以下任何内容的指针:

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

现在,您的结构显然与前三个项目符号都不匹配。并且也不匹配最后的项目符号,因为数组是托管类型。

据我了解,我的结构的所有属性都是 blittable 属性。

是的,这是真的,但不相关。您需要结构的成员不仅仅是 blittable。


即使您的代码可以编译,您如何想象它可以工作?考虑这个表达式:

*(Results*)packet

编译器如何将其转换为可以创建新数组并复制正确数量的数组元素的东西?很明显,编译器没有希望在这里做任何有用的事情,这当然是语言拒绝你的代码的原因。

我认为不安全的代码在这里不会对您有所帮助。当你序列化你的数组时,你必须序列化长度,然后是数组的内容。要反序列化,您需要读取长度,创建该长度的新数组,然后读取内容。不安全的代码对此无能为力。静态定义类型的简单内存副本是没有用的,因为这意味着数组的长度在编译时是已知的。它不是。


关于您的更新,您说:

我有一个结果对象数组,我使用二进制序列化对其进行了序列化。

为了反序列化,您需要了解二进制序列化详细布局的代码。问题中的代码无法做到这一点。

您可能还没有理解的是,您不能期望复制任意的内存块,其长度是可变的并且仅在运行时才知道,而实际上却不知道这些长度。实际上,您希望能够在系统中不知道要复制多少的情况下复制内存。

您尝试使用不安全的类型转换和内存副本进行反序列化是行不通的。如果不考虑序列化的二进制格式,您将无法获得更详细的帮助。

于 2014-04-21T08:15:39.913 回答