C# BinaryFormatter bytes orde

て烟熏妆下的殇ゞ 提交于 2019-12-01 12:50:12

问题


I am using binary formatter in order to serialize my object. I would like to know what is the order of the properties in the serialized byte array (according to properties order in the object class? randomaly?) And if I can control the order of the bytes according to the props.

For example,
If I serialize the following obj:

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

If I will serialize it, what is the order of bytes means? (does the first 4 bytes will represent the age, and the next are the weight? and so on.. or the binary formatter set it randomaly)


回答1:


Why don't you just try it? Lets take your class

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

And serialize it, then inspect the result by examining the 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());
}

This will give:

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  .

That is the convoluted format Hans is talking about. If you squint a bit you recognize an assemblyname, the classname, the fieldnames (kind of) and if you apply the magic offered by jdweng you notice the 4 bytes 2A 00 00 00 which would make 42 (Age) and the next 4 bytes represent -1 (Weight).

Let's add a public field Name as the first field:

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

and let's look at the changed bytes:

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  .

That seems to make sense. Let's put that field at the end:

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

and the result is:

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  .

No change at all.

One final example to convince you that the output of the BinaryFormatter is an implementation detail and that serializing and deserializing should be left to that class and is not be attempted by other means.

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

And if we initialize that class as follows:

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

the hexdump will show this:

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.

Notice the order of Address and _name although the actual values of the string[] array are put at the end.

So to answer your question:

I would like to know what is the order of the properties in the serialized byte array (according to properties order in the object class? randomly?)

It is an implementation detail that depends on the type of the field and its order in the class. It's metadata and actual value might be in a different order as well. It is not randomly and it is not the order in the class.

And if I can control the order of the bytes according to the props.

It might seems you can control it to some extent but this is so much of an implementation detail that it is not practical to try to influence it, predict it or rely on it.

Keep in mind that you can only serialize and deserialize the specific version of the class. There is no backward compatibility.

If you need to have strict control over the serialization format use an open standard, like XML, JSON or proto-buf. Or roll your own serializer, leveraging the BinaryWriter as suggested by Peter.



来源:https://stackoverflow.com/questions/48041390/c-sharp-binaryformatter-bytes-orde

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!