Search code examples
c#serializationnetdatacontractserializer

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


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.


Solution

  • 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.