C# DataContract 和序列化只读布尔字段始终为 'true'

C# DataContract and Serializing readonly bool field to always be 'true'

下面的class是一个人为的例子。我希望在每次序列化时将 'isVisible' 字段序列化为 'true' 的最简单方法...无论它设置为什么。

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

目前,我想到了两个办法。

  1. 创建一个 'OnSerializing()' 方法,在序列化期间使用反射将 属性 设置为 'true'。 (我不确定这是否会起作用……但看起来会起作用)。

  2. 在启动 class 序列化的方法中...在序列化之前创建具有 isVisible 的所有新项目作为 'true'。

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

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

您希望在反序列化期间将字段 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 as this is 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;
        }
    }
    

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