【问题标题】:C# DataContract and Serializing readonly bool field to always be 'true'C# DataContract 和序列化只读布尔字段始终为“真”
【发布时间】:2018-04-08 07:05:43
【问题描述】:

下面的类是一个人为的例子。我想要在每次序列化时将“isVisible”字段序列化为“true”的最简单方法......不管它设置为什么。

在实际代码中...这是一个更大的对象,并且有几个不同的构造函数,它们从不同的点调用。

现在,我能想到两种方法。

  1. 创建一个“OnSerializing()”方法,该方法在序列化期间使用反射将属性设置为“true”。 (我实际上不确定这是否会奏效……但似乎会奏效)。

  2. 在启动类序列化的方法中...在序列化之前创建所有 isVisible 为“真”的新项目。

    [DataContract]
    public class MapItem
    {
        public MapItem(bool isVisible)
        {
            this.isVisible = isVisible;
        }
    
        [DataMember]
        private readonly bool isVisible;
    
        public bool IsVisible => isVisible;
    }
    

我为什么需要这个?基本上,我需要加载这些项目并让它们在加载后始终直接可见。在运行应用程序时... MapItems 可能会变得可见/不可见。因此,我希望 XML 始终具有 <isVisible>true</isVisible> 或者我希望 MapItem 中的值仅在反序列化时为“真”。

【问题讨论】:

  • 我想要在每次序列化时将 'isVisible' 字段序列化为 'true' 的最简单方法 -- 你的意思是你希望 <isVisible>true</isVisible> 出现在序列化的 XML,或者您希望在序列化期间将 MapItem 类中的实际字段值修改为 true
  • 如果你总是希望它是true,为什么你可以选择它是假的?你真的需要序列化它吗?您可以将其默认为 true 并使用 IgnoreDataMemberAttribute 忽略 IsVisible 吗?
  • @dbc 是的,我希望 XML 始终具有 true 或者我希望 MapItem 中的值仅在反序列化时为“true”。基本上,我需要加载这些项目并让它们在加载后始终直接可见。运行应用程序时...地图项可能会变得可见/不可见。
  • @TyCobb 如上所述。在运行应用程序时,这些项目可能可见或不可见。只有在加载时,我才需要将它们全部设置为可见。但是,我认为这样做更省时的方法是将它们保存为可见而不是以这种方式加载它们。
  • 这里的基本问题是DataContractSerializer doesn't call your constructor,因此您没有方便的方法将只读bool字段初始化为true。你愿意将字段的含义从isVisible 翻转到isNotVisible 吗?在这种情况下,默认值将是正确的值。

标签: c# serialization datacontractserializer


【解决方案1】:

您希望在反序列化期间将字段 private readonly bool isVisible; 初始化为 true。不幸的是,DataContractSerializer doesn't call any constructor of your class 所以没有明显、简单的方法可以做到这一点。

您提出的解决方案——在序列化期间始终发出 <isVisible>true</isVisible> 并利用数据协定序列化程序反序列化只读字段值的能力——并不理想,因为 XML 设计不当文件,例如<isVisible>false 或什至丢失的一个可能会在您的应用程序中引入意外行为。

请考虑以下不需要序列化 ​​isVisible 值的替代方案:

  1. 修改只读字段的名称和语义,使默认值是正确的、所需的值。在这种情况下,您需要将当前的 isVisible 替换为 private readonly bool isNotVisible;

    [DataContract]
    public class MapItem
    {
        // Do not mark with [DataContract] as deserialized instances should always have the default value
        private readonly bool isNotVisible; 
    
        public MapItem(bool isVisible)
        {
            this.isNotVisible = !isVisible;
        }
    
        public bool IsVisible { get { return !isNotVisible; } }
    }
    
  2. 使用反射在[OnDeserializing] 回调中设置字段值:

    [DataContract]
    public class MapItem
    {
        public MapItem(bool isVisible)
        {
            this.isVisible = isVisible;
        }
    
        // Do not mark with [DataContract] as deserialized instances should always have the value
        // set in the OnDeserializing() callback.
        private readonly bool isVisible;
    
        public bool IsVisible { get { return isVisible; } }
    
        [OnDeserializing]
        void OnDeserializing(StreamingContext context)
        {
            typeof(MapItem)
                .GetField(nameof(isVisible), BindingFlags.Instance | BindingFlags.NonPublic)
                .SetValue(this, true);
        }
    }
    
  3. 最后,您可以实现ISerializable,因为这是supported by the data contract serializer,并在流式构造函数中将isVisible初始化为true

    [Serializable]
    public class MapItem : ISerializable
    {
        public MapItem(bool isVisible)
        {
            this.isVisible = isVisible;
        }
    
        private readonly bool isVisible;
    
        public bool IsVisible { get { return isVisible; } }
    
        #region ISerializable Members
    
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
        }
    
        #endregion
    
        protected MapItem(SerializationInfo info, StreamingContext context)
        {
            this.isVisible = true;
        }
    }
    

    但是不推荐这样做,因为您现在需要手动序列化您的所有班级成员。

【讨论】:

    猜你喜欢
    • 2013-11-16
    • 1970-01-01
    • 2017-07-04
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 2013-04-26
    • 2022-07-31
    相关资源
    最近更新 更多