在一个特殊应用中,我们需要将内存中的一个对象持久化,而这个对象是来自一个模板类实例化出来的,不能保存到数据库中,数据库中只存有此对象的模板.
由于使用到泛型的Dictionary,而XmlSerializer却不支持默认的泛型的Dictionary,为此我找了些资料,并在此文中以三种不同的方式实现. 本文中约定:
方案1: 不序列化泛型的Dictionary
方案2: 定义支持泛型的Dictionary
方案3: 让每个类实现IXmlSerializable接口
本文内容:
1. 类图及类之间的关联
2. 方案1: 不序列化泛型的Dictionary
3. 方案2: 定义支持泛型的Dictionary
4. 方案3: 让每个类实现IXmlSerializable接口
5. 总结
1. 类图及类之间的关联
薪酬模板PayTemplate,它通过Dictionary<int,PayItemTemplate>维持着多个薪酬栏目模板PayItemTemplate.
而一个PayItemTemplate通过Dictionary<int,PayItemTemplate>记录着那些使用过它的PayItemTemplate.
所以,PayTemplate与PayItemTemplate是一对多关联; PayItemTemplate是一对多的自关联.
方案1,2,3中的类关联都相似,这里就不重复了. 详细差别可在本文末尾下载源代码查看.
2. 方案1: 不序列化泛型的Dictionary
既然知道了泛型的Dictionary不被XmlSerializer支持,我们就避免泛型的Dictionary被序列化,只需要在字段上加上XmlIgnore属性即可.代码如下:
46@H_403_35@ [XmlIgnore@H_403_35@]//带有XmlIgnore,表示序列化时不序列化此属性@H_403_35@
47@H_403_35@ public@H_403_35@Dictionary@H_403_35@<int@H_403_35@,PayItemTemplate@H_403_35@> PayItemTemplateDic
48@H_403_35@ {
49@H_403_35@ get@H_403_35@{return@H_403_35@payItemTemplates; }
50@H_403_35@ set@H_403_35@{ payItemTemplates =value@H_403_35@; }
51@H_403_35@ }
好了,既然让泛型的Dictionary不被序列化了,而我们的需求中又需要将泛型的Dictionary中的对象序列化到Xml中保存,那怎么办呢?这里的办法就是加多一个额外的PayItemTemplate[]数组字段,代码实现如下:
53@H_403_35@ ///@H_403_35@@H_403_35@<summary>@H_403_35@
54@H_403_35@ ///@H_403_35@用于序列化PayItemTemplate集合@H_403_35@
55@H_403_35@ ///@H_403_35@@H_403_35@</summary>@H_403_35@
56@H_403_35@ public@H_403_35@PayItemTemplate@H_403_35@[] PayItemTemplates
57@H_403_35@ {
59@H_403_35@ {
60@H_403_35@ List@H_403_35@<PayItemTemplate@H_403_35@> list =new@H_403_35@List@H_403_35@<PayItemTemplate@H_403_35@>(payItemTemplates.Count);
61@H_403_35@ foreach@H_403_35@(KeyValuePair@H_403_35@<int@H_403_35@,PayItemTemplate@H_403_35@> pitin@H_403_35@payItemTemplates)
62@H_403_35@ {
63@H_403_35@ list.Add(pit.Value);
64@H_403_35@ }
65@H_403_35@
66@H_403_35@ return@H_403_35@list.ToArray();
67@H_403_35@ }
69@H_403_35@ {
70@H_403_35@ payItemTemplates =new@H_403_35@Dictionary@H_403_35@<int@H_403_35@,PayItemTemplate@H_403_35@>();
71@H_403_35@ foreach@H_403_35@(PayItemTemplate@H_403_35@pitin@H_403_35@value@H_403_35@)
72@H_403_35@ {
73@H_403_35@ payItemTemplates.Add(pit.Id,pit);
74@H_403_35@ }
75@H_403_35@ }
76@H_403_35@ }
这就是我们需要做的,下面进行测试,具体的单元测试,可在下载源代码中查看.
测试结果:
正向序列化:
<?@H_403_35@xml@H_403_35@@H_403_35@version@H_403_35@=@H_403_35@"1.0@H_403_35@"@H_403_35@encoding@H_403_35@=@H_403_35@"utf-8@H_403_35@"?>@H_403_35@
<@H_403_35@PayTemplate@H_403_35@@H_403_35@xmlns:xsi@H_403_35@=@H_403_35@"http://www.w3.org/2001/XMLSchema-instance@H_403_35@"@H_403_35@xmlns:xsd@H_403_35@=@H_403_35@"http://www.w3.org/2001/XMLSchema@H_403_35@">@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@100</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模版Test</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@StartDate@H_403_35@>@H_403_35@2007-08-16T19:10:43.640625+08:00</@H_403_35@StartDate@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplates@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplate@H_403_35@>@H_403_35@
<@H_403_35@numbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@numbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@10000</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模板栏目1</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateRelieds@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplate@H_403_35@>@H_403_35@
<@H_403_35@numbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@numbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@20000</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模板栏目2</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateRelieds@H_403_35@/>@H_403_35@
<@H_403_35@Enabled@H_403_35@>@H_403_35@true</@H_403_35@Enabled@H_403_35@>@H_403_35@
<@H_403_35@Expression@H_403_35@>@H_403_35@Expression2</@H_403_35@Expression@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplate@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplateRelieds@H_403_35@>@H_403_35@
<@H_403_35@Enabled@H_403_35@>@H_403_35@true</@H_403_35@Enabled@H_403_35@>@H_403_35@
<@H_403_35@Expression@H_403_35@>@H_403_35@Expression</@H_403_35@Expression@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplate@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplate@H_403_35@>@H_403_35@
<@H_403_35@numbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@numbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@20000</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模板栏目2</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateRelieds@H_403_35@/>@H_403_35@
<@H_403_35@Enabled@H_403_35@>@H_403_35@true</@H_403_35@Enabled@H_403_35@>@H_403_35@
<@H_403_35@Expression@H_403_35@>@H_403_35@Expression2</@H_403_35@Expression@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplate@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplates@H_403_35@>@H_403_35@
<@H_403_35@WorkFlowCategory@H_403_35@>@H_403_35@Simple</@H_403_35@WorkFlowCategory@H_403_35@>@H_403_35@
方案1优缺点: 不用定义额外的对象,但出现多余的字段(多余是因为此字段只是为了序列化而出现的).
3. 方案2: 定义支持泛型的Dictionary
是否能让泛型的Dictionary能够被正常序列化呢? 是,重新定义一个泛型的SerializableDictionary,让它继承.Net 中的泛型Dictionary并实现IXmlSerializable接口,本文中的实现参考自XML Serializable Generic Dictionary. IXmlSerializable接口中的两个关键方法:
25@H_403_35@ ///@H_403_35@@H_403_35@<summary>@H_403_35@
26@H_403_35@ ///@H_403_35@反序列化@H_403_35@
27@H_403_35@ ///@H_403_35@@H_403_35@</summary>@H_403_35@
28@H_403_35@ ///@H_403_35@@H_403_35@<param name="reader"></param>@H_403_35@
29@H_403_35@ public@H_403_35@void@H_403_35@ReadXml(XmlReader@H_403_35@reader)
30@H_403_35@ {
31@H_403_35@ XmlSerializer@H_403_35@keySerializer =new@H_403_35@XmlSerializer@H_403_35@(typeof@H_403_35@(TKey));
32@H_403_35@ XmlSerializer@H_403_35@valueSerializer =new@H_403_35@XmlSerializer@H_403_35@(typeof@H_403_35@(TValue));
33@H_403_35@ if@H_403_35@(reader.IsEmptyElement || !reader.Read())
34@H_403_35@ {
35@H_403_35@ return@H_403_35@;
36@H_403_35@ }
37@H_403_35@
38@H_403_35@ while@H_403_35@(reader.NodeType !=XmlNodeType@H_403_35@.EndElement)
39@H_403_35@ {
40@H_403_35@ reader.ReadStartElement("item"@H_403_35@);
41@H_403_35@
42@H_403_35@ reader.ReadStartElement("key"@H_403_35@);
43@H_403_35@ TKey key = (TKey)keySerializer.Deserialize(reader);
44@H_403_35@ reader.ReadEndElement();
45@H_403_35@
46@H_403_35@ reader.ReadStartElement("value"@H_403_35@);
47@H_403_35@ TValue value = (TValue)valueSerializer.Deserialize(reader);
48@H_403_35@ reader.ReadEndElement();
49@H_403_35@
50@H_403_35@ reader.ReadEndElement();
51@H_403_35@ reader.MoveToContent();
52@H_403_35@
53@H_403_35@ this@H_403_35@.Add(key,value);
54@H_403_35@ }
55@H_403_35@ reader.ReadEndElement();
56@H_403_35@ }
57@H_403_35@
58@H_403_35@ ///@H_403_35@@H_403_35@<summary>@H_403_35@
59@H_403_35@ ///@H_403_35@序列化@H_403_35@
60@H_403_35@ ///@H_403_35@@H_403_35@</summary>@H_403_35@
61@H_403_35@ ///@H_403_35@@H_403_35@<param name="writer"></param>@H_403_35@
62@H_403_35@ public@H_403_35@void@H_403_35@WriteXml(XmlWriter@H_403_35@writer)
63@H_403_35@ {
64@H_403_35@ XmlSerializer@H_403_35@keySerializer =new@H_403_35@XmlSerializer@H_403_35@(typeof@H_403_35@(TKey));
65@H_403_35@ XmlSerializer@H_403_35@valueSerializer =new@H_403_35@XmlSerializer@H_403_35@(typeof@H_403_35@(TValue));
66@H_403_35@
67@H_403_35@ foreach@H_403_35@(TKey keyin@H_403_35@this@H_403_35@.Keys)
68@H_403_35@ {
69@H_403_35@ writer.WriteStartElement("item"@H_403_35@);
70@H_403_35@
71@H_403_35@ writer.WriteStartElement("key"@H_403_35@);
72@H_403_35@ keySerializer.Serialize(writer,key);
73@H_403_35@ writer.WriteEndElement();
74@H_403_35@
75@H_403_35@ writer.WriteStartElement("value"@H_403_35@);
76@H_403_35@ valueSerializer.Serialize(writer,this@H_403_35@[key]);
77@H_403_35@ writer.WriteEndElement();
78@H_403_35@
79@H_403_35@ writer.WriteEndElement();
80@H_403_35@ }
81@H_403_35@ }
<?@H_403_35@xml@H_403_35@@H_403_35@version@H_403_35@=@H_403_35@"1.0@H_403_35@"@H_403_35@encoding@H_403_35@=@H_403_35@"utf-8@H_403_35@"?>@H_403_35@
<@H_403_35@PayTemplateV2@H_403_35@@H_403_35@xmlns:xsi@H_403_35@=@H_403_35@"http://www.w3.org/2001/XMLSchema-instance@H_403_35@"@H_403_35@xmlns:xsd@H_403_35@=@H_403_35@"http://www.w3.org/2001/XMLSchema@H_403_35@">@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@100</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模版Test</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@StartDate@H_403_35@>@H_403_35@2007-08-16T19:08:45.0625+08:00</@H_403_35@StartDate@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplates@H_403_35@>@H_403_35@
<@H_403_35@item@H_403_35@>@H_403_35@
<@H_403_35@key@H_403_35@>@H_403_35@
<@H_403_35@int@H_403_35@>@H_403_35@10000</@H_403_35@int@H_403_35@>@H_403_35@
</@H_403_35@key@H_403_35@>@H_403_35@
<@H_403_35@value@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateV2@H_403_35@>@H_403_35@
<@H_403_35@numbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@numbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@10000</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模板栏目1</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateReliedList@H_403_35@>@H_403_35@
<@H_403_35@item@H_403_35@>@H_403_35@
<@H_403_35@key@H_403_35@>@H_403_35@
<@H_403_35@int@H_403_35@>@H_403_35@20000</@H_403_35@int@H_403_35@>@H_403_35@
</@H_403_35@key@H_403_35@>@H_403_35@
<@H_403_35@value@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateV2@H_403_35@>@H_403_35@
<@H_403_35@numbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@numbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@20000</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模板栏目2</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateReliedList@H_403_35@/>@H_403_35@
<@H_403_35@Enabled@H_403_35@>@H_403_35@true</@H_403_35@Enabled@H_403_35@>@H_403_35@
<@H_403_35@Expression@H_403_35@>@H_403_35@Expression2</@H_403_35@Expression@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplateV2@H_403_35@>@H_403_35@
</@H_403_35@value@H_403_35@>@H_403_35@
</@H_403_35@item@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplateReliedList@H_403_35@>@H_403_35@
<@H_403_35@Enabled@H_403_35@>@H_403_35@true</@H_403_35@Enabled@H_403_35@>@H_403_35@
<@H_403_35@Expression@H_403_35@>@H_403_35@Expression</@H_403_35@Expression@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplateV2@H_403_35@>@H_403_35@
</@H_403_35@value@H_403_35@>@H_403_35@
</@H_403_35@item@H_403_35@>@H_403_35@
<@H_403_35@item@H_403_35@>@H_403_35@
<@H_403_35@key@H_403_35@>@H_403_35@
<@H_403_35@int@H_403_35@>@H_403_35@20000</@H_403_35@int@H_403_35@>@H_403_35@
</@H_403_35@key@H_403_35@>@H_403_35@
<@H_403_35@value@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateV2@H_403_35@>@H_403_35@
<@H_403_35@numbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@numbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@20000</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模板栏目2</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateReliedList@H_403_35@/>@H_403_35@
<@H_403_35@Enabled@H_403_35@>@H_403_35@true</@H_403_35@Enabled@H_403_35@>@H_403_35@
<@H_403_35@Expression@H_403_35@>@H_403_35@Expression2</@H_403_35@Expression@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplateV2@H_403_35@>@H_403_35@
</@H_403_35@value@H_403_35@>@H_403_35@
</@H_403_35@item@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplates@H_403_35@>@H_403_35@
<@H_403_35@WorkFlowCategory@H_403_35@>@H_403_35@Simple</@H_403_35@WorkFlowCategory@H_403_35@>@H_403_35@
反向序列化:
方案2中我们定义了SerializableDictionary并实现了IXmlSerializable接口,惟一的不足就是需要将源代码中的所有需要序列化的泛型Dictionary改为SerializableDictionary. 好在有VS2005开发工具帮忙,只需要点击查找-全部替换就行了; 并且SerializableDictionary具有可重用性.
4. 方案3: 让每个类实现IXmlSerializable接口
此方案类似于方案2,只是没有重新定义新的Dictionary,而是让每个带有泛型Dictionary且需要Xml序列化的类实现IXmlSerializable接口,自行定义Xml序列化逻辑. 实现代码与方案2中的类似. 这里就省略了,具体可下载源代码查看.
测试结果:
正向序列化:
<?@H_403_35@xml@H_403_35@@H_403_35@version@H_403_35@=@H_403_35@"1.0@H_403_35@"@H_403_35@encoding@H_403_35@=@H_403_35@"utf-8@H_403_35@"?>@H_403_35@
<@H_403_35@PayTemplateV3@H_403_35@>@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@100</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模版Test</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@StartDate@H_403_35@>@H_403_35@2007年8月17日</@H_403_35@StartDate@H_403_35@>@H_403_35@
<@H_403_35@WorkFlowCategory@H_403_35@>@H_403_35@Simple</@H_403_35@WorkFlowCategory@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplates@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateV3@H_403_35@>@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@10000</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模板栏目1</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@Expression@H_403_35@>@H_403_35@Expression</@H_403_35@Expression@H_403_35@>@H_403_35@
<@H_403_35@Enabled@H_403_35@>@H_403_35@True</@H_403_35@Enabled@H_403_35@>@H_403_35@
<@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateReliedList@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateV3@H_403_35@>@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@20000</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模板栏目2</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@Expression@H_403_35@>@H_403_35@Expression2</@H_403_35@Expression@H_403_35@>@H_403_35@
<@H_403_35@Enabled@H_403_35@>@H_403_35@True</@H_403_35@Enabled@H_403_35@>@H_403_35@
<@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateReliedList@H_403_35@/>@H_403_35@
</@H_403_35@PayItemTemplateV3@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplateReliedList@H_403_35@>@H_403_35@
</@H_403_35@PayItemTemplateV3@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateV3@H_403_35@>@H_403_35@
<@H_403_35@Id@H_403_35@>@H_403_35@20000</@H_403_35@Id@H_403_35@>@H_403_35@
<@H_403_35@Name@H_403_35@>@H_403_35@薪酬模板栏目2</@H_403_35@Name@H_403_35@>@H_403_35@
<@H_403_35@Expression@H_403_35@>@H_403_35@Expression2</@H_403_35@Expression@H_403_35@>@H_403_35@
<@H_403_35@Enabled@H_403_35@>@H_403_35@True</@H_403_35@Enabled@H_403_35@>@H_403_35@
<@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@Calculational</@H_403_35@NumbericalCategory@H_403_35@>@H_403_35@
<@H_403_35@PayItemTemplateReliedList@H_403_35@/>@H_403_35@
</@H_403_35@PayItemTemplateV3@H_403_35@>@H_403_35@
反向序列化:
为什么我还要使用方案3呢? 因为在序列化时有可能会出现循环引用的情况,一旦出现了,我们就只能使用方案3了. 又或者在序列化时,有些类是需要进行特殊处理的,我们都可以采用此方法来实现. 方案3显然是灵活性最高的,但代码工作量较多.
5. 总结
使用.Net 2.0中的XmlSerializer类,可以方便地将对象转换成Xml格式,本文介绍简单一个序列化应用需求,而XmlSerializer还有许多更高级的功能,如串行化属性设置等.
XmlSerializer并不直接支持泛型Dictionary(Dictionary在XML序列化时遇到的问题及应对方案),所有我在方案1中,公开了一个数组属性,只是用于序列化时使用; 而方案2中,重新定义了一个支持Xml序列化的泛型Dictionary,参考自XML Serializable Generic Dictionary; 方案3是最灵活但工作最多的一种方案,为每个需要Xml序列化的类实现IXmlSerializable接口,然后自定义如何序列化,过程可参考方案2.
完.
:) 希望这对你有帮助.