如何在C#中将对象转换为字节数组

chu*_*gan 86 c#

我有一组对象需要写入二进制文件.

我需要文件中的字节是紧凑的,所以我不能使用BinaryFormatter. BinaryFormatter抛出反序列化需求的各种信息.

如果我试试

byte[] myBytes = (byte[]) myObject 
Run Code Online (Sandbox Code Playgroud)

我得到一个运行时异常.

我需要快速,所以我宁愿不复制字节数组.我只是喜欢演员表演byte[] myBytes = (byte[]) myObject!

好的只是为了清楚,我输出文件中没有任何元数据.只是对象字节.打包的对象到对象.根据收到的答案,看起来我将编写低级Buffer.BlockCopy代码.也许使用不安全的代码.

Cry*_*ics 150

将对象转换为字节数组:

// Convert an object to a byte array
public static byte[] ObjectToByteArray(Object obj)
{
    BinaryFormatter bf = new BinaryFormatter();
    using (var ms = new MemoryStream())
    {
        bf.Serialize(ms, obj);
        return ms.ToArray();
    }
}
Run Code Online (Sandbox Code Playgroud)

您只需将此函数复制到您的代码中,然后将需要转换为字节数组的对象发送给它.如果需要再次将字节数组转换为object,可以使用下面的函数:

// Convert a byte array to an Object
public static Object ByteArrayToObject(byte[] arrBytes)
{
    using (var memStream = new MemoryStream())
    {
        var binForm = new BinaryFormatter();
        memStream.Write(arrBytes, 0, arrBytes.Length);
        memStream.Seek(0, SeekOrigin.Begin);
        var obj = binForm.Deserialize(memStream);
        return obj;
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以将此功能与自定义类一起使用.您只需在类中添加[Serializable]属性即可启用序列化

  • 我尝试了这个,它添加了各种元数据.OP表示他不想要元数据. (8认同)
  • 现在认为使用二进制格式化程序是不安全的。https://docs.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide#preferred-alternatives (7认同)
  • 更不用说,每个人似乎都认为你试图序列化的东西是你已经编写过的,或者已经预先设置为序列化的东西. (4认同)
  • 在第二个代码示例中,您可以将字节数组直接传递给`MemoryStream` 的构造函数。这将消除使用 `Write(...)` 和 `Seek(...)`。 (3认同)

Guf*_*ffa 38

如果您希望序列化数据非常紧凑,您可以自己编写序列化方法.这样你就可以减少开销.

例:

public class MyClass {

   public int Id { get; set; }
   public string Name { get; set; }

   public byte[] Serialize() {
      using (MemoryStream m = new MemoryStream()) {
         using (BinaryWriter writer = new BinaryWriter(m)) {
            writer.Write(Id);
            writer.Write(Name);
         }
         return m.ToArray();
      }
   }

   public static MyClass Desserialize(byte[] data) {
      MyClass result = new MyClass();
      using (MemoryStream m = new MemoryStream(data)) {
         using (BinaryReader reader = new BinaryReader(m)) {
            result.Id = reader.ReadInt32();
            result.Name = reader.ReadString();
         }
      }
      return result;
   }

}
Run Code Online (Sandbox Code Playgroud)

  • @Smith:使用`BinaryWriter/Reader`你自己进行序列化/反序列化,你可以尽可能紧凑地写/读绝对需要的数据.`BinaryFormatter`使用反射来找出要写入/读取的数据,并使用适用于所有可能情况的格式.它还包括有关流中格式的元信息,因此会增加更多开销. (3认同)
  • @Smith:是的,你可以这样做,只需一个接一个地写。`BinaryWriter` 将以 `BinaryReader` 可以读取的格式写入它们,只要您以相同的顺序写入和读取它们。 (2认同)
  • @Smith:您可以将枚举转换为 `int`(或者如果您已指定任何其他类型作为枚举的存储)并写入它。当您阅读它时,您可以将其转换为枚举类型。 (2认同)

Jon*_*eet 31

那么从铸造myObjectbyte[]永远不会工作,除非你有一个明确的转换,或者myObject 一个byte[].你需要的序列化框架的一些实物.那里有很多,包括近乎亲爱的Protocol Buffers.在空间和时间方面,它都非常"精益和平均".

你会发现几乎所有的序列化框架都对你可以序列化的内容有很大的限制,但是 - 由于跨平台,Protocol Buffers比一些更多.

如果你能提出更多要求,我们可以为你提供更多帮助 - 但它永远不会像铸造一样简单......

编辑:只是回应这个:

我需要我的二进制文件来包含对象的字节.只有字节,没有任何元数据.打包的对象到对象.所以我将实现自定义序列化.

请记住,对象中的字节经常是引用...因此您需要弄清楚如何处理它们.

我怀疑你会发现设计和实现自己的自定义序列化框架比你想象的更难.

我个人建议,如果您只需要针对几种特定类型执行此操作,那么您就不必费心试图提出一般的序列化框架.只需在所需的所有类型中实现实例方法和静态方法:

public void WriteTo(Stream stream)
public static WhateverType ReadFrom(Stream stream)
Run Code Online (Sandbox Code Playgroud)

要记住的一件事:如果你有继承,一切都变得更加棘手.没有继承,如果您知道自己的类型,则不需要包含任何类型信息.当然,还有版本控制的问题 - 您是否需要担心不同版本的类型的向后和向前兼容性?

  • *零*元数据的风险是你非常*版本不容忍,因为它很少有允许灵活性的方法,为时已晚.协议缓冲区数据密集.你真的需要额外转动螺丝吗? (6认同)

小智 31

现在认为使用二进制格式化程序是不安全的。请参阅 -->微软文档

只需使用 System.Text.Json:

序列化为字节:

JsonSerializer.SerializeToUtf8Bytes(obj);

要反序列化为您的类型:

JsonSerializer.Deserialize(byteArray);


Mar*_*ell 13

你真的在谈论序列化,它可以采取多种形式.由于您需要小型和二进制,协议缓冲区可能是一个可行的选择 - 同时提供版本容错和可移植性.与此不同BinaryFormatter,协议缓冲区有线格式不包括所有类型元数据; 只是非常简洁的标记来识别数据.

在.NET中有一些实现; 特别是

我谦虚地认为protobuf-net(我写的)允许更多的.NET习惯用法与典型的C#类("常规"协议缓冲区倾向于要求代码生成); 例如:

[ProtoContract]
public class Person {
   [ProtoMember(1)]
   public int Id {get;set;}
   [ProtoMember(2)]
   public string Name {get;set;}
}
....
Person person = new Person { Id = 123, Name = "abc" };
Serializer.Serialize(destStream, person);
...
Person anotherPerson = Serializer.Deserialize<Person>(sourceStream);
Run Code Online (Sandbox Code Playgroud)


jhi*_*den 13

我接受了Crystalonics的回答并将其转换为扩展方法.我希望其他人会发现它们很有用:

public static byte[] SerializeToByteArray(this object obj)
{
    if (obj == null)
    {
        return null;
    }
    var bf = new BinaryFormatter();
    using (var ms = new MemoryStream())
    {
        bf.Serialize(ms, obj);
        return ms.ToArray();
    }
}

public static T Deserialize<T>(this byte[] byteArray) where T : class
{
    if (byteArray == null)
    {
        return null;
    }
    using (var memStream = new MemoryStream())
    {
        var binForm = new BinaryFormatter();
        memStream.Write(byteArray, 0, byteArray.Length);
        memStream.Seek(0, SeekOrigin.Begin);
        var obj = (T)binForm.Deserialize(memStream);
        return obj;
    }
}
Run Code Online (Sandbox Code Playgroud)


The*_*rga 5

这对我有用:

byte[] bfoo = (byte[])foo;
Run Code Online (Sandbox Code Playgroud)

foo 是一个我 100% 确定它是一个字节数组的对象。