C#BinaryFormatter字节序

DBS*_*DBS 0 .net c# serialization binaryformatter deserialization

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

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

public class Human
{
     int Age {get;set;}
     int Weight {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

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

ren*_*ene 5

你为什么不试试呢?让我们上课

[Serializable]
public class Human
{
    public int Age {get;set;}
    public int Weight {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

并序列化它,然后通过检查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());
}
Run Code Online (Sandbox Code Playgroud)

这将给出:

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  .
Run Code Online (Sandbox Code Playgroud)

这就是汉斯所谈论的令人费解的格式。如果斜视一下,就可以识别一个程序集名称,类名称,字段名称(种类),并且如果使用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;}   
}
Run Code Online (Sandbox Code Playgroud)

让我们看一下更改后的字节:

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  .
Run Code Online (Sandbox Code Playgroud)

这似乎是有道理的。让我们将该字段放在最后:

[Serializable]
public class Human
{
    public int Age {get;set;}
    public int Weight {get; set;}   
    public string Name;
}
Run Code Online (Sandbox Code Playgroud)

结果是:

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  .
Run Code Online (Sandbox Code Playgroud)

完全没有变化。

最后一个示例使您确信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;}}
}
Run Code Online (Sandbox Code Playgroud)

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

new Human{ Name ="Test", Age = 42, Weight = -1, Address =new []{"foo","bar"}}
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

请注意地址和_name的顺序,尽管string []数组的实际值放在末尾。

因此,回答您的问题:

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

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

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

似乎您可以在某种程度上控制它,但这只是实现细节,以至于无法尝试影响,预测或依赖它。

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

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