Serialization and Deserialization When References Are Involved in C#
I have a singleton class called Manager that holds a list of object instances:
static class Manager
{
static List<Foo> Foos = new List<Foo>();
}
I then have a collection of object instances using a class called Meter that uses references to items in the list Foos:
class Meter
{
public Foo MyFoo = null;
}
...
public void CreateMeter(int Us开发者_如何学JAVAerChoice)
{
Meter MyMeter = new Meter();
MyMeter.MyFoo = Manager.Foos[UserChoice];
}
When the application saves a project file it serializes the instances of Foo in Foos along with all the instances of Meter.
My problem is how to deserialize this arrangement. Currently I do the following:
- Deserialize the project-wide instances of Foo to reconstruct Manager.Foos
- Deserialize a meter instance which includes an instance of Foo for the MyFoo property
- Search Manager.Foos and find the matching reference for MyMeter.MyFoo and then assign the reference from Manager.Foos.
This seems to me clunky and not too easy to extend. I would rather that the Meter factory doesn't need to search Manager.Foos during deserialization because in future Meter might take it's refence to a Foo instance from other places, not just Manager.
Is there a simple but flexible alternative solution to this deserialization problem where references to objects can be easily reconstructed?
Serialisation is hard :)
Doing this automatically in a way that doesn't mess with the format is a pain. A common trick here is to assign an unrelated opaque key while de/serialising, using a central map. You can see this in DataContractSerializer by enabling reference-tracking in the constructor. This key is then used to check for existing objects as a substitute.
Personally, when it gets that complex IMO it is time to use a pre-canned aerializer; Even inside a dedicated library it is a bit challenging. The approach I use (protobuf-net) is pretty similar, but harder to read (binary dense output, etc).
in Manager class add
public static ulong IdProvider=0
in Foo class add
public ulong MyId
in Foo ctor add
MyId=Manager.IdProvider++;
each Foo class will have a unique id ( up to 2^64-1 instances), just save the IdProvider so you could continue to add unique ids upon reconstruction.
You might want to consider changing the Meter class to hold the Foo's unique id and provide a property (get;set) to link to the "Foos" in the Manager class.
I haven't completely understood what you want but from this point of view you've made quite a mess :p
Thanks Marc and everyone else for the suggestions. Based on Marc's key idea and Henk's object id generator here is what I have so far.
Foo defines a Guid property which contains a unique Guid. This is generated in the constructor but can also be saved/restored with serialization/deserialization.
class Foo
{
public Guid TheGuid = Guid.NewGuid();
}
Meter no longer uses a reference, instead it uses a Guid:
class Meter
{
public Guid FooGuid;
}
When a meter is created the Guid establishes the connection:
public void CreateMeter(int UserChoice)
{
Meter MyMeter = new Meter();
MyMeter.FooGuid = Manager.Foos[UserChoice].TheGuid;
}
When Meter is serialized/deserialized the Guid is stored/loaded.
One downside is when the Foo instance associated with a meter needs to be accessed. Instead of directly using the reference a lookup has to be performed which will incur a performance hit:
class Manager
{
public List<Foo> Foos = new List<Foo>();
public Foo GetFooFromGuid(Guid SearchGuid)
{
// search Foos and return instance with Guid == SearchGuid
}
}
A benefit of this approach is that now I can create a delegate and have multiple sources of Foo to associate with meters:
Func<Guid, Foo> FooSource;
FooSource ManagerFooSource = Manager.GetFooFromGuid;
精彩评论