我正在使用二进制格式化程序来序列化我的对象。 我想知道序列化字节数组中属性的顺序是什么(根据对象类中的属性顺序?randomaly?) 如果我可以根据道具控制字节的顺序。
例如,
如果我序列化以下obj:
public class Human
{
int Age {get;set;}
int Weight {get; set;}
}
如果我将序列化,那么字节的顺序是什么意思? (前4个字节是代表年龄,下一个是权重吗?依此类推......或者二进制格式化程序将其设置为randomaly)
答案 0 :(得分:4)
你为什么不试试呢?让我们上课
[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 .
这就是汉斯所说的错综复杂的格式。如果你斜视一下,你会认出一个程序集名称,类名,字段名(种类),如果你应用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。