C# DataContract Serialization, how to deserialize to already existing instance

Stefan Steinegger picture Stefan Steinegger · Dec 21, 2009 · Viewed 15.1k times · Source

I have a class, which holds a static dictionary of all existing instances, which are defined at compile time.

Basically it looks like this:

[DataContract]
class Foo
{
  private static Dictionary<long, Foo> instances = new Dictionary<long, Foo>();

  [DataMember]
  private long id;

  public static readonly Foo A = Create(1);
  public static readonly Foo B = Create(2);
  public static readonly Foo C = Create(3);

  private static Foo Create(long id)
  {
    Foo instance = new Foo();
    instance.id = id;
    instances.Add(instance);
    return instance;
  }

  public static Foo Get(long id)
  {
    return instances[id];
  }    

}

There are other fields, and the class is derived, but this doesn't matter for the problem.

Only the id is serialized. When an instance of this type is deserialized, I would like to get the instance that has been created as the static field (A, B or C), using Foo.Get(id) instead of getting a new instance.

Is there a simple way to do this? I didn't find any resources which I was able to understand.

Answer

Marc Gravell picture Marc Gravell · Jan 2, 2010

During deserialization it (AFAIK) always uses a new object (FormatterServices.GetUninitializedObject), but to get it to substitute the objects after deserialization (but before they are returned to the caller), you can implement IObjectReference, like so:

[DataContract]
class Foo : IObjectReference { // <===== implement an extra interface
    object IObjectReference.GetRealObject(StreamingContext ctx) {
        return Get(id);
    }
    ...snip
}

done... proof:

static class Program {
    static void Main() {
        Foo foo = Foo.Get(2), clone;
        DataContractSerializer ser = new DataContractSerializer(typeof(Foo));
        using (MemoryStream ms = new MemoryStream()) { // clone it via DCS
            ser.WriteObject(ms, foo);
            ms.Position = 0;
            clone = (Foo)ser.ReadObject(ms);
        }
        Console.WriteLine(ReferenceEquals(foo, clone)); // true
    }
}

Note there are some extra notes on this for partial trust scenarios on MSDN, here.