1

我有以下代码:

    [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 或当我用反射填充我的属性时 -> 序列化失败,当我删除此代码部分时 -> 序列化工作但当然没有我的值。

这是真的?有人知道我的解决方案的替代方案吗?

它应该根据描述自动从数据库中获取一些东西,当它什么都不返回时,它会回退到 DefaultValue...

PS1:我正在使用 XmlSerializer

PS2:当我在序列化之前设置断点时,我可以看到所有属性都填充了好的值(如 71、72 等)。

4

1 回答 1

1

现在奇怪的是:当我尝试序列化这个类时,我会得到一个空节点 CustomClass!

XmlSerializer用于DefaultValue决定要序列化哪些值 - 如果它与默认值匹配,则不存储它。这种方法与数据绑定/模型绑定等类似模型一致。

坦率地说,我会说在这种情况下,两者DefaultValueAttribute都是DescriptionAttribute糟糕的选择。写你自己的 - 也许EavInitAttribute- 然后使用类似的东西:

[EavInit(41, "name")]
public uint Name {get;set;}

请注意,还有其他方法可以控制此条件序列化 - 您可以编写如下方法:

public bool ShouldSerializeName() { return true; }

将有助于说服它写入值(这是另一种被各种序列化和数据绑定 API 识别的模式) - 但坦率地说,这是更多的工作(它是每个属性,并且需要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 ?? "";
        }
    }
}
于 2013-08-23T09:01:16.127 回答