/// <summary> /// An opportunity to clear an instance before caching it for future reuse as a cloning or deserialization target. /// The method is expected to call Serializer.Clear on all reference-type fields. /// </summary> /// <param name="target">The instance to clear.</param> /// <param name="context">A context object containing accumulated type mappings and object references.</param> public override void Clear(ref T target, SerializationContext context) { // null? if (target == null) { return; } // is it an object that we have already seen? int id; if (context.GetOrAddSerializedObjectId(target, out id)) { target = (T)context.GetDeserializedObject(id); return; } // is it a polymorphic object? Type instanceType = target.GetType(); if (typeof(T) != instanceType) { // determine and use the correct serialization handler based on the runtime type object objTarget = target; Serializer.Clear(instanceType, ref objTarget, context); target = (T)objTarget; } else { // call the serializer to clear the fields this.InnerClear(ref target, context); } }
/// <summary> /// Creates a deep clone of the given object. /// </summary> /// <param name="instance">The instance to clone.</param> /// <param name="target">An optional existing instance to clone into.</param> /// <param name="context">A context object containing accumulated type and object references.</param> public override void Clone(T instance, ref T target, SerializationContext context) { // null? if (instance == null) { target = instance; return; } // is it an object that we have already seen? int id; if (context.GetOrAddSerializedObjectId(instance, out id)) { // find and reuse the object that we have already cloned in the object cache target = (T)context.GetDeserializedObject(id); if (target == null) { throw new InvalidDataException("The serializer detected a circular reference: " + typeof(T)); } return; } // did we use this target before? If so, we can't use it again. if (target != null && context.ContainsDeserializedObject(target)) { target = default(T); } // is it a polymorphic object? Type instanceType = instance.GetType(); if (typeof(T) != instanceType) { // determine and use the correct serialization handler based on the runtime type object objTarget = target; Serializer.Clone(instanceType, instance, ref objTarget, context); target = (T)objTarget; } else { // not polymorphic, invoke the serializer to clone the fields this.InnerClone(instance, ref target, context); } return; }
// deserialize the ref envelope (null, duplicate reference, circular reference, polymorphism), before deserializing the object itself public override void Deserialize(BufferReader reader, ref T target, SerializationContext context) { // read the header uint prefix = reader.ReadUInt32(); switch (prefix & RefPrefixMask) { // null? case RefPrefixNull: { target = default(T); return; } // is it an object that we have already seen? case RefPrefixExisting: { // find the object in the object cache and return it int id = (int)(prefix & RefPrefixValueMask); target = (T)context.GetDeserializedObject(id); if (target == null) { // a custom serializer was implemented incorrectly throw new InvalidDataException($"The serializer detected an unresolved circular reference to an instance of type {typeof(T)}. The custom serializer for this type needs to implement the ISerializerEx interface."); } return; } // is it a polymorphic object? case RefPrefixTyped: { // did we use this target before? If so, we can't use it again. if (target != null && context.ContainsDeserializedObject(target)) { target = default(T); } // determine the correct handler of the instance to deserialize from the prefix int handlerId = (int)(prefix & RefPrefixValueMask); var handler = context.Serializers.GetUntypedHandler(handlerId, typeof(T)); // determine and use the correct serialization handler object objTarget = target; handler.UntypedDeserialize(reader, ref objTarget, context); target = (T)objTarget; return; } // not polymorphic and not seen before case RefPrefixNew: { // did we use this target before? If so, we can't use it again. if (target != null && context.ContainsDeserializedObject(target)) { target = default(T); } // invoke the serializer to read the fields this.InnerDeserialize(reader, ref target, context); return; } } }