设计模式-C#中涉及引用时的序列化和反序列化

本文关键字:序列化 反序列化 引用 -C# 设计模式 | 更新日期: 2023-09-27 18:00:56

我有一个名为Manager的单例类,它包含一个对象实例列表:

static class Manager
{
    static List<Foo> Foos = new List<Foo>();
}

然后,我有一个使用名为Meter的类的对象实例集合,该类使用对Foos:列表中项目的引用

class Meter
{
    public Foo MyFoo = null;
}
...
public void CreateMeter(int UserChoice)
{
    Meter MyMeter = new Meter();
    MyMeter.MyFoo = Manager.Foos[UserChoice];
}

当应用程序保存项目文件时,它会序列化Foo中的Foo实例以及Meter的所有实例。

我的问题是如何反序列化这种安排。目前我做以下工作:

  • 反序列化Foo的项目范围实例以重建Manager.FFoos
  • 反序列化包含MyFoo属性的Foo实例的仪表实例
  • 搜索Manager.Fos并找到MyMeter.MyFoo的匹配引用,然后从Manager.Foos分配引用

在我看来,这似乎很笨拙,而且不太容易扩展。我宁愿Meter工厂在反序列化过程中不需要搜索Manager.Fos,因为将来Meter可能会从其他地方引用Foo实例,而不仅仅是Manager。

对于这个反序列化问题,是否有一个简单但灵活的替代解决方案,可以轻松地重建对对象的引用?

设计模式-C#中涉及引用时的序列化和反序列化

串行化很难:(

以一种不会打乱格式的方式自动完成这项工作是一件痛苦的事。这里的一个常见技巧是,在去串行化/串行化时,使用中心映射来分配一个不相关的不透明密钥。通过在构造函数中启用引用跟踪,可以在DataContractSerializer中看到这一点。然后,该键用于检查现有对象作为替代项。

就我个人而言,当IMO变得如此复杂时,是时候使用预先罐装的空气呼吸器了;即使在一个专门的图书馆里,这也有点挑战性。我使用的方法(protobuf-net(非常相似,但更难阅读(二进制密集输出等(。

在Manager类中添加

public static ulong IdProvider=0

在Foo类中添加

public ulong MyId

在Foo ctor中添加

MyId=Manager.IdProvider++;

每个Foo类都将有一个唯一的id(最多2^64-1个实例(,只需保存IdProvider,就可以在重建时继续添加唯一的id。

您可能需要考虑更改Meter类以保存Foo的唯一id,并提供一个属性(get;set(来链接到Manager类中的"Foo"。

我还没有完全理解你想要什么,但从这个角度来看,你已经把事情搞得一团糟:p

感谢Marc和其他人的建议。基于Marc的关键思想和Henk的对象id生成器,这里是我迄今为止所拥有的。

Foo定义了一个包含唯一Guid的Guid属性。这是在构造函数中生成的,但也可以通过序列化/反序列化来保存/还原。

class Foo
{
    public Guid TheGuid = Guid.NewGuid();
}

Meter不再使用引用,而是使用Guid:

class Meter
{
    public Guid FooGuid;
}

创建仪表时,Guid建立连接:

public void CreateMeter(int UserChoice)
{
    Meter MyMeter = new Meter();
    MyMeter.FooGuid = Manager.Foos[UserChoice].TheGuid;
}

当Meter被序列化/反序列化时,Guid被存储/加载。

一个缺点是需要访问与仪表关联的Foo实例。必须执行查找,而不是直接使用引用,这将导致性能打击:

class Manager
{
    public List<Foo> Foos = new List<Foo>();
    public Foo GetFooFromGuid(Guid SearchGuid)
    {
        // search Foos and return instance with Guid == SearchGuid
    }
}

这种方法的一个好处是,现在我可以创建一个委托,并有多个Foo源与电表关联:

Func<Guid, Foo> FooSource;
FooSource ManagerFooSource = Manager.GetFooFromGuid;