Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

.net - C# BinaryFormatter bytes orde

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)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...