【问题标题】:Preserving restrictive access modifiers and making a class serializable保留限制性访问修饰符并使类可序列化
【发布时间】:2019-11-18 20:25:09
【问题描述】:

我正在尝试使我编写的类可序列化。它包含几个静态只读字段,在构建后永远不会改变,因此它们不需要序列化。我很难找到一种在反序列化后重新设置它们的方法,这种方法不会削弱基类中的访问。这是一个简化的示例,从不可序列化的基类和派生类开始:

internal abstract class MyBase
{
    private readonly List<int> myIntList = new List<int> ();

    internal MyBase (List<int> anIntList)
    {
        this.myIntList = anIntList;
    }
}

派生类不需要访问列表,所以该字段在基类中可以是private readonly,派生类这样设置:

internal sealed class MyDerived : MyBase
{
    private static readonly List<int> derivedIntList = new List<int> { 1, 2, 3 };

    internal MyDerived () : base (MyDerived.derivedIntList)
    {
    }
}

现在我想让我的派生类可序列化。由于列表内容没有变化,所以不需要序列化它们,所以我只是在两个类上都放了一个 DataContract 属性。

我像这样将派生类序列化到磁盘:

private static void SeralizeDerived (string path)
{
    MyDerived derived = new MyDerived ();

    DataContractSerializer serializer = new DataContractSerializer (typeof (MyDerived));

    using (FileStream stream = new FileStream (path, FileMode.Create))
    {
        serializer.WriteObject (stream, derived);
        stream.Flush ();
    }
}

并像这样反序列化它:

private static void DeserializeDerived (string path)
{
    DataContractSerializer serializer = new DataContractSerializer (typeof (MyDerived));

    using (FileStream stream = new FileStream (path, FileMode.Open, FileAccess.Read))
    {
        MyDerived derived = serializer.ReadObject (stream) as MyDerived;
        // debugger shows derived.myIntList as null, as expected
    }
}

根据 DeserializeDerived 中的注释,derived.myIntList 的值为 null。这对我来说并不奇怪——我没有要求它被序列化,我也没有做任何事情来在反序列化后重新创建它。

问题是这样的:我知道解决此问题的唯一方法是更改​​对 myIntList 的访问以使其受到保护,并在 MyDerived 类中有一个 OnDeserialized 方法(重新)设置 myIntList:

private void ReInitialize ()
{
    base.myIntList = MyDerived.derivedIntList;
}

[OnDeserialized]
private void OnDeserialized (StreamingContext context)
{
    this.ReInitialize ();
}

internal MyDerived () : base ()
{
    this.ReInitialize ();
}

这感觉不对——我不想削弱对基类成员的访问,并且在派生构造函数中逐字段初始化成员更容易出错。问题是:如何保持相同的成员保护但仍支持序列化?

【问题讨论】:

标签: c# serialization encapsulation access-specifier


【解决方案1】:

将静态列表传递给基类构造函数对我来说看起来很奇怪。我会说这里的继承有问题。

如果您仍想使用抽象属性的继承尝试方法:

public abstract class Data
{
    public abstract int[] list { get;}
}

public class Data2 : Data
{
    private static readonly int[] arr = new[] {1,2,3};
    public override int[] list { get => arr; }
}

【讨论】:

  • 谢谢 mtkachenko。我应该在我的示例中提到派生类实际上根本不需要访问列表——它只在基类中使用。有多个 MyDerived,它们都传入不同的列表。如果我正确阅读了您的示例,则您已授予 Data2 对列表的访问权限,我可以这样做,但它会减少封装。
  • @RichardDrysdall 当您需要覆盖派生类中的某些属性/方法但主要算法在基类 sourcemaking.com/design_patterns/template_method 中描述时,这仍然是一个经典案例。在我的示例中,我强制派生类覆盖抽象 list 属性,因为它是基类所必需的。当然你可以使用protected 代替public。封装没有问题。
  • 谢谢 mtkachenko - 我还没有意识到与模板的联系。我已经尝试了您的示例,并且效果很好。我会尝试看看我是否可以使用模板重新实现我的类,但至少我现在有一些可以工作的东西。
  • 只读数组是这里的重点,当然还有硬编码数组。但是如何解决例如带有未知成员数组的 json 反序列化?
猜你喜欢
  • 2014-03-06
  • 2019-04-27
  • 2020-02-05
  • 1970-01-01
  • 1970-01-01
  • 2014-07-31
  • 2013-03-09
  • 2013-04-11
  • 2013-03-05
相关资源
最近更新 更多