【问题标题】:C# BinaryFormatter bytes ordeC# BinaryFormatter 字节顺序
【发布时间】:2018-06-11 00:04:57
【问题描述】:

我正在使用二进制格式化程序来序列化我的对象。 我想知道序列化字节数组中属性的顺序是什么(根据对象类中的属性顺序?随机?) 如果我可以根据道具控制字节的顺序。

例如,
如果我序列化以下obj:

public class Human
{
     int Age {get;set;}
     int Weight {get; set;}
}

如果我要序列化它,字节顺序是什么意思? (前4个字节是否代表年龄,接下来是权重?依此类推..或二进制格式化程序随机设置)

【问题讨论】:

  • 如果你使用BinaryFormatter来序列化数据,你应该使用它来反序列化数据,所以字节顺序无关紧要。 BinaryFormatter 会为您处理好一切。如果您希望使用 BinaryFormatter 以外的其他方式处理数据,则应该使用其他方式来序列化数据。例如。 BinaryWriter,使用小端序。坦率地说,不是很清楚你在问什么;这个问题可以从字面上理解,但您似乎真的有一个更广泛的问题。
  • 您的问题也令人困惑,因为您似乎主要询问字节顺序(字节序),但您似乎也在询问 值的顺序也是。如果您对解释 BinaryFormatter 输出有某些特定需求,如果您包含 minimal reproducible example 显示您如何尝试解释它,解释如果出现任何问题,以及具体是什么,您的问题将会更好地被接受您正在展示您想了解的输出。
  • 我明白了,谢谢!
  • 属性按照类中属性的顺序排列。您可以在 get 方法中自定义保存顺序。下面显示了字节顺序: int Age = 51230000; byte[] bytes = BitConverter.GetBytes(Age);整数 = (3 * 16777216) + (13 * 65536) + (181 * 256) + 48;
  • 没有订单。 BinaryFormatter 使用复杂的数据格式,它还存储有关正在序列化的对象的信息。就像对象中字段的名称和类型来自的程序集一样。严格来说,所有这些都是故意没有记录的实现细节。你不应该关心。

标签: c# .net serialization deserialization binaryformatter


【解决方案1】:

你为什么不试试呢?来上课吧

[Serializable]
public class Human
{
    public int Age {get;set;}
    public int Weight {get; set;}
}

并对其进行序列化,然后通过检查 HexDump 来检查结果

var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
using(var ms = new MemoryStream())
{
  bf.Serialize(ms, new Human{ Age = 42, Weight = -1 });
  HexDump(ms.ToArray());
}

这将给出:

00000 : 00 01 00 00 00 FF FF FF FF 01 00 00 00 00 00 00  .....????.......
00016 : 00 0C 02 00 00 00 43 71 75 65 72 79 5F 6C 68 68  ......Cquery_lhh
00032 : 75 78 68 2C 20 56 65 72 73 69 6F 6E 3D 30 2E 30  uxh, Version=0.0
00048 : 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65  .0.0, Culture=ne
00064 : 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79  utral, PublicKey
00080 : 54 6F 6B 65 6E 3D 6E 75 6C 6C 05 01 00 00 00 0F  Token=null......
00096 : 55 73 65 72 51 75 65 72 79 2B 48 75 6D 61 6E 02  UserQuery+Human.
00112 : 00 00 00 14 3C 41 67 65 3E 6B 5F 5F 42 61 63 6B  ....<Age>k__Back
00128 : 69 6E 67 46 69 65 6C 64 17 3C 57 65 69 67 68 74  ingField.<Weight
00144 : 3E 6B 5F 5F 42 61 63 6B 69 6E 67 46 69 65 6C 64  >k__BackingField
00160 : 00 00 08 08 02 00 00 00 2A 00 00 00 FF FF FF FF  ........*...????
00176 : 0B  .

这就是 Hans 所说的复杂格式。如果你眯着眼睛,你会认出一个程序集名、类名、字段名(某种),如果你应用 jdweng 提供的魔法,你会注意到 4 个字节 2A 00 00 00 这将使 42(年龄)和接下来的 4 个字节代表 - 1(重量)。

让我们添加一个公共字段Name作为第一个字段:

[Serializable]
public class Human
{
    public string Name;
    public int Age {get;set;}
    public int Weight {get; set;}   
}

让我们看看改变的字节:

00096 : 55 73 65 72 51 75 65 72 79 2B 48 75 6D 61 6E 03  UserQuery+Human.
00112 : 00 00 00 04 4E 61 6D 65 14 3C 41 67 65 3E 6B 5F  ....Name.<Age>k_
00128 : 5F 42 61 63 6B 69 6E 67 46 69 65 6C 64 17 3C 57  _BackingField.<W
00144 : 65 69 67 68 74 3E 6B 5F 5F 42 61 63 6B 69 6E 67  eight>k__Backing
00160 : 46 69 65 6C 64 01 00 00 08 08 02 00 00 00 06 03  Field...........
00176 : 00 00 00 04 54 65 73 74 2A 00 00 00 FE FF FF FF  ....Test*...????
00192 : 0B  .

这似乎是有道理的。让我们把那个字段放在最后:

[Serializable]
public class Human
{
    public int Age {get;set;}
    public int Weight {get; set;}   
    public string Name;
}

结果是:

00096 : 55 73 65 72 51 75 65 72 79 2B 48 75 6D 61 6E 03  UserQuery+Human.
00112 : 00 00 00 04 4E 61 6D 65 14 3C 41 67 65 3E 6B 5F  ....Name.<Age>k_
00128 : 5F 42 61 63 6B 69 6E 67 46 69 65 6C 64 17 3C 57  _BackingField.<W
00144 : 65 69 67 68 74 3E 6B 5F 5F 42 61 63 6B 69 6E 67  eight>k__Backing
00160 : 46 69 65 6C 64 01 00 00 08 08 02 00 00 00 06 03  Field...........
00176 : 00 00 00 04 54 65 73 74 2A 00 00 00 FE FF FF FF  ....Test*...????
00192 : 0B  .

完全没有变化。

最后一个示例让您相信 BinaryFormatter 的输出是一个实现细节,序列化和反序列化应该留给该类,而不是通过其他方式尝试。

[Serializable]
public class Human
{
    public string[] Address; 
    private string _name;

    public int Weight {get; set;} // switched
    public int Age {get;set;}

    public string Name {get{return _name;} set{_name=value;}}
}

如果我们按如下方式初始化该类:

new Human{ Name ="Test", Age = 42, Weight = -1, Address =new []{"foo","bar"}}

hexdump 会显示这个:

00096 : 55 73 65 72 51 75 65 72 79 2B 48 75 6D 61 6E 04  UserQuery+Human.
00112 : 00 00 00 07 41 64 64 72 65 73 73 05 5F 6E 61 6D  ....Address._nam
00128 : 65 17 3C 57 65 69 67 68 74 3E 6B 5F 5F 42 61 63  e.<Weight>k__Bac
00144 : 6B 69 6E 67 46 69 65 6C 64 14 3C 41 67 65 3E 6B  kingField.<Age>k
00160 : 5F 5F 42 61 63 6B 69 6E 67 46 69 65 6C 64 06 01  __BackingField..
00176 : 00 00 08 08 02 00 00 00 09 03 00 00 00 06 04 00  ................
00192 : 00 00 04 54 65 73 74 FF FF FF FF 2A 00 00 00 11  ...Test????*....
00208 : 03 00 00 00 02 00 00 00 06 05 00 00 00 03 66 6F  ..............fo
00224 : 6F 06 06 00 00 00 03 62 61 72 0B  o......bar.

请注意 Address 和 _name 的顺序,尽管 string[] 数组的实际值放在最后。

所以回答你的问题:

我想知道序列化字节数组中属性的顺序是什么(根据对象类中的属性顺序?随机?)

这是一个实现细节,取决于字段的类型及其在类中的顺序。它的元数据和实际值也可能有不同的顺序。不是随机的,也不是类中的顺序。

如果我可以根据道具控制字节的顺序。

您似乎可以在一定程度上控制它,但这是一个非常详细的实现细节,试图影响它、预测它或依赖它是不切实际的。

请记住,您只能对特定版本的类进行序列化和反序列化。没有向后兼容性。

如果您需要严格控制序列化格式,请使用开放标准,例如 XML、JSON 或 proto-buf。或者使用 Peter 建议的 BinaryWriter 滚动您自己的序列化程序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 2018-08-09
    • 1970-01-01
    • 2010-12-10
    相关资源
    最近更新 更多