#region Copyright 2010-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
namespace CSharpTest.Net.Cloning
{
///
/// Used to duplicate objects either by the ISerializable interface or by field-level duplication.
///
public class SerializerClone : MemberwiseClone
{
readonly IDictionary _fieldsCache;
readonly IList _callbacks;
static readonly StreamingContext _sCtx = new StreamingContext(StreamingContextStates.Persistence);
///
/// Used to duplicate objects either by the ISerializable interface or by field-level duplication.
///
public SerializerClone()
{
_fieldsCache = new Dictionary();
_callbacks = new List();
}
///
/// Public entry point to begin duplication of the object graph. If your using this instance multiple
/// times you should call Clear() between the object graphs or the copies previously made will be used.
///
public override T Clone(T instance)
{
T result;
try
{
result = base.Clone(instance);
foreach (IDeserializationCallback cb in _callbacks)
cb.OnDeserialization(this);
}
finally
{
if (_callbacks.Count > 0)
_callbacks.Clear();
}
return result;
}
///
/// If the object provided is [Serializable] a simulated serialization routine is used to duplicate
/// the object, if it's not serializable then the MemberwiseClone base class will perform the copy.
///
protected override object CloneDefault(object instance)
{
Type type = instance.GetType();
if (!type.IsSerializable)
return base.CloneDefault(instance);
object copy;
if (instance is ISerializable)
copy = CloneWithISerializable(instance);
else
copy = CloneSerializableFields(instance);
if (copy is IDeserializationCallback)
_callbacks.Add((IDeserializationCallback)copy);
return copy;
}
private object CloneSerializableFields(object instance)
{
object copy;
Type type = instance.GetType();
MemberInfo[] fields = SerializedFields(type);
object[] values = FormatterServices.GetObjectData(instance, fields);
copy = FormatterServices.GetUninitializedObject(type);
Graph.Add(instance, copy);
for (int i = 0; i < values.Length; i++)
values[i] = this.CloneObject(values[i]);
FormatterServices.PopulateObjectMembers(copy, fields, values);
return copy;
}
private object CloneWithISerializable(object instance)
{
object copy;
Type type = instance.GetType();
SerializationInfo sInfo = new SerializationInfo(type, new FormatterConverter());
((ISerializable)instance).GetObjectData(sInfo, _sCtx);
Type tcopy = Type.GetType(String.Format("{0}, {1}", sInfo.FullTypeName, sInfo.AssemblyName), true, false);
copy = FormatterServices.GetUninitializedObject(tcopy);
Graph.Add(instance, copy);
SerializationInfo sCopy = new SerializationInfo(tcopy, new FormatterConverter());
foreach (SerializationEntry se in sInfo)
sCopy.AddValue(se.Name, this.CloneObject(se.Value), se.ObjectType);
if (type == tcopy || typeof(ISerializable).IsAssignableFrom(tcopy))
{
ConstructorInfo ci = tcopy.GetConstructor(
BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null,
new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);
if (ci == null)
throw new SerializationException("Serialization constructor not found.");
ci.Invoke(copy, new object[] { sCopy, _sCtx });
}
else
{
List fields = new List();
List