我有以下代码:
[Serializable]
public class CustomClass
{
public CustomClass()
{
this.Init();
}
public void Init()
{
foreach (PropertyInfo p in this.GetType().GetProperties())
{
DescriptionAttribute da = null;
DefaultValueAttribute dv = null;
foreach (Attribute attr in p.GetCustomAttributes(true))
{
if (attr is DescriptionAttribute)
{
da = (DescriptionAttribute) attr;
}
if (attr is DefaultValueAttribute)
{
dv = (DefaultValueAttribute) attr;
}
}
UInt32 value = 0;
if (da != null && !String.IsNullOrEmpty(da.Description))
{
value = Factory.Instance.SelectByCode(da.Description, 3);
}
if (dv != null && value == 0)
{
value = (UInt32) dv.Value;
}
p.SetValue(this, value, null);
}
}
private UInt32 name;
[Description("name")]
[DefaultValue(41)]
public UInt32 Name
{
get { return this.name; }
set { this.name = value; }
}
(30 more properties)
}
现在奇怪的是:当我尝试序列化这个类时,我会得到一个空节点CustomClass!
<CustomClass />
当我从构造函数中删除Init时,它按预期工作!我将得到类的完整xml表示但是没有值(所有值为0)。
<CustomClass>
<Name>0</Name>
...
</CustomClass>
另外,当我注释掉Init的主体时,我将得到与上面相同的内容(具有默认值的那个) 我已经尝试了一个公共方法,Helper类的一切,但它不起作用。也就是说,而不是预期的:
<CustomClass>
<Name>15</Name>
...
</CustomClass>
我会得到
<CustomClass />
当我在这个类中使用反射时,似乎无法进行序列化。 或者总结一下:当我调用Init或用反射填充我的属性时 - &gt;序列化失败,当我删除此代码部分 - &gt;序列化工作但当然没有我的价值观。
这是真的吗?有人知道我的解决方案的另一种选择吗?
它应该根据描述自动从数据库中获取内容,当它返回任何内容时,它会回退到DefaultValue ...
PS1:我正在使用XmlSerializer
PS2:当我在序列化之前设置断点时,我可以看到所有属性都填充了良好的值(如71,72等)。
答案 0 :(得分:1)
现在奇怪的是:当我尝试序列化这个类时,我会得到一个空节点CustomClass!
XmlSerializer
使用DefaultValue
来决定序列化哪些值 - 如果它与默认值匹配,则不会存储。这种方法与数据绑定/模型绑定等类似模型一致。
坦率地说,我会说在这种情况下,DefaultValueAttribute
和DescriptionAttribute
都是不好的选择。写你自己的 - 也许是EavInitAttribute
- 然后使用类似的东西:
[EavInit(41, "name")]
public uint Name {get;set;}
请注意,有其他方法来控制此条件序列化 - 您可以编写如下方法:
public bool ShouldSerializeName() { return true; }
也工作以说服它写入值(这是各种序列化和数据绑定API识别的另一种模式) - 但坦率地说这是更多的工作(它是per-property ,并且需要public
,因此它会使API变得混乱。
最后,我会说每次新对象构建多次访问数据库(每个属性一次)是非常昂贵的 - 特别是因为很多这些值很可能会在中分配值 >(所以查找它们是浪费精力)。如果是我的话,我会花很多心思把这个“懒惰”和“缓存”。
懒惰和“稀疏”实现的一个例子:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Xml.Serialization;
static class Program
{
static void Main()
{
var obj = new CustomClass();
Console.WriteLine(obj.Name);
// show it working via XmlSerializer
new XmlSerializer(obj.GetType()).Serialize(Console.Out, obj);
}
}
public class CustomClass : EavBase
{
[EavInit(42, "name")]
public uint Name
{
get { return GetEav(); }
set { SetEav(value); }
}
}
public abstract class EavBase
{
private Dictionary<string, uint> values;
protected uint GetEav([CallerMemberName] string propertyName = null)
{
if (values == null) values = new Dictionary<string, uint>();
uint value;
if (!values.TryGetValue(propertyName, out value))
{
value = 0;
var prop = GetType().GetProperty(propertyName);
if (prop != null)
{
var attrib = (EavInitAttribute)Attribute.GetCustomAttribute(
prop, typeof(EavInitAttribute));
if (attrib != null)
{
value = attrib.DefaultValue;
if (!string.IsNullOrEmpty(attrib.Key))
{
value = LookupDefaultValueFromDatabase(attrib.Key);
}
}
}
values.Add(propertyName, value);
}
return value;
}
protected void SetEav(uint value, [CallerMemberName] string propertyName = null)
{
(values ?? (values = new Dictionary<string, uint>()))[propertyName] = value;
}
private static uint LookupDefaultValueFromDatabase(string key)
{
// TODO: real code here
switch (key)
{
case "name":
return 7;
default:
return 234;
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
protected class EavInitAttribute : Attribute
{
public uint DefaultValue { get; private set; }
public string Key { get; private set; }
public EavInitAttribute(uint defaultValue) : this(defaultValue, "") { }
public EavInitAttribute(string key) : this(0, key) { }
public EavInitAttribute(uint defaultValue, string key)
{
DefaultValue = defaultValue;
Key = key ?? "";
}
}
}