pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>));
public static class Cloner<T>
where T : class, new()
{
private static Func<T, T> _cloneDelegate;
private static Func<PropertyInfo, bool> _clonePropertyDelegate;
private static Func<T, T> CloneDelegate
{
get
{
if (_cloneDelegate == null)
_cloneDelegate = GenerateCloneMethod().CreateDelegate(typeof(Func<T, T>)) as Func<T, T>;
return _cloneDelegate;
}
}
public static Func<PropertyInfo, bool> ClonePropertyDelegate
{
get { return _clonePropertyDelegate; }
set
{
if (_clonePropertyDelegate != value)
{
_clonePropertyDelegate = value;
_cloneDelegate = null;
}
}
}
private static DynamicMethod GenerateCloneMethod()
{
var dynamicMethod = new DynamicMethod("Clone", typeof(T), new Type[] { typeof(T) });
var cloneIlGenerator = dynamicMethod.GetILGenerator();
var value = cloneIlGenerator.DeclareLocal(typeof(T));
cloneIlGenerator.Emit(OpCodes.Ldarg_0);
var argNotNullLabel = cloneIlGenerator.DefineLabel();
cloneIlGenerator.Emit(OpCodes.Brtrue_S, argNotNullLabel);
cloneIlGenerator.Emit(OpCodes.Ldnull);
cloneIlGenerator.Emit(OpCodes.Ret);
cloneIlGenerator.MarkLabel(argNotNullLabel);
cloneIlGenerator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(new Type[0]));
cloneIlGenerator.Emit(OpCodes.Stloc_S, value);
CopyProps(cloneIlGenerator, value, typeof(T), () => cloneIlGenerator.Emit(OpCodes.Ldarg_0));
cloneIlGenerator.Emit(OpCodes.Ldloc_S, value);
cloneIlGenerator.Emit(OpCodes.Ret);
return dynamicMethod;
}
private static void CopyProps(ILGenerator cloneIlGenerator, LocalBuilder lb, Type typeT, Action getSource)
{
foreach (var prop in typeT.GetProperties().Where(p => p.CanRead && p.CanWrite && (ClonePropertyDelegate == null || ClonePropertyDelegate(p))))
{
if (typeof(ComplexObject).IsAssignableFrom(prop.PropertyType))
{
ConstructorInfo complexObjectCtor = prop.PropertyType.GetConstructor(new Type[0]);
if (complexObjectCtor != null)
{
cloneIlGenerator.Emit(OpCodes.Ldloc_S, lb);
var sourceComplexTypeProp = cloneIlGenerator.DeclareLocal(prop.PropertyType);
var valueComplexTypeProp = cloneIlGenerator.DeclareLocal(prop.PropertyType);
getSource();
cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod("get_" + prop.Name));
cloneIlGenerator.Emit(OpCodes.Stloc_S, sourceComplexTypeProp);
cloneIlGenerator.Emit(OpCodes.Newobj, complexObjectCtor);
cloneIlGenerator.Emit(OpCodes.Stloc_S, valueComplexTypeProp);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, valueComplexTypeProp);
cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod("set_" + prop.Name));
CopyProps(cloneIlGenerator, valueComplexTypeProp, prop.PropertyType, () => cloneIlGenerator.Emit(OpCodes.Ldloc, sourceComplexTypeProp));
}
}
else if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(EntityReference<>))
CopyEntityReference(cloneIlGenerator, () => cloneIlGenerator.Emit(OpCodes.Ldloc, lb), typeT, getSource, prop, false);
else
{
cloneIlGenerator.Emit(OpCodes.Ldloc_S, lb);
getSource();
cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod("get_" + prop.Name));
cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod("set_" + prop.Name));
}
}
}
internal static void CopyEntityReference(ILGenerator cloneIlGenerator, Action loadNewObject, Type typeT, Action getSource, PropertyInfo prop, bool copyNull)
{
var sourceEntityReferenceEntityKeyProp = cloneIlGenerator.DeclareLocal(typeof(EntityKey));
var sourceEntityReferenceEntityKeyMemberProp = cloneIlGenerator.DeclareLocal(typeof(EntityKeyMember));
var sourceEntityReferenceEntityKeysMemberProp = cloneIlGenerator.DeclareLocal(typeof(EntityKeyMember[]));
var sourceEntityReferenceEntityKeysMemberLength = cloneIlGenerator.DeclareLocal(typeof(int));
var valueEntityReferenceProp = cloneIlGenerator.DeclareLocal(prop.PropertyType);
var valueEntityReferenceEntityKeyProp = cloneIlGenerator.DeclareLocal(typeof(EntityKey));
var valueEntityReferenceEntityKeysMemberProp = cloneIlGenerator.DeclareLocal(typeof(EntityKeyMember[]));
var loopIndex = cloneIlGenerator.DeclareLocal(typeof(int));
loadNewObject();
cloneIlGenerator.Emit(OpCodes.Newobj, prop.PropertyType.GetConstructor(new Type[0]));
cloneIlGenerator.Emit(OpCodes.Stloc_S, valueEntityReferenceProp);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, valueEntityReferenceProp);
cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod("set_" + prop.Name));
getSource();
cloneIlGenerator.Emit(OpCodes.Callvirt, typeT.GetMethod("get_" + prop.Name));
cloneIlGenerator.Emit(OpCodes.Callvirt, prop.PropertyType.GetMethod("get_EntityKey"));
cloneIlGenerator.Emit(OpCodes.Stloc_S, sourceEntityReferenceEntityKeyProp);
var entityKeyNullLabel = cloneIlGenerator.DefineLabel();
cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeyProp);
cloneIlGenerator.Emit(OpCodes.Brfalse, entityKeyNullLabel);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, valueEntityReferenceProp);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeyProp);
cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityKey).GetMethod("get_EntityKeyValues")); // We suppose EntityKeyValues is not null
cloneIlGenerator.Emit(OpCodes.Stloc_S, sourceEntityReferenceEntityKeysMemberProp);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeysMemberProp);
cloneIlGenerator.Emit(OpCodes.Ldlen);
cloneIlGenerator.Emit(OpCodes.Conv_I4);
cloneIlGenerator.Emit(OpCodes.Stloc_S, sourceEntityReferenceEntityKeysMemberLength);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeysMemberLength);
cloneIlGenerator.Emit(OpCodes.Newarr, typeof(EntityKeyMember));
cloneIlGenerator.Emit(OpCodes.Stloc_S, valueEntityReferenceEntityKeysMemberProp);
var noEntityKeyValues = cloneIlGenerator.DefineLabel();
cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeysMemberLength);
cloneIlGenerator.Emit(OpCodes.Brfalse_S, noEntityKeyValues);
cloneIlGenerator.Emit(OpCodes.Ldc_I4_0);
cloneIlGenerator.Emit(OpCodes.Stloc_S, loopIndex);
var startLoopLabel = cloneIlGenerator.DefineLabel();
cloneIlGenerator.MarkLabel(startLoopLabel);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, loopIndex);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeysMemberLength);
cloneIlGenerator.Emit(OpCodes.Ceq);
var endOfLoopLable = cloneIlGenerator.DefineLabel();
cloneIlGenerator.Emit(OpCodes.Brtrue_S, endOfLoopLable);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeysMemberProp);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, loopIndex);
cloneIlGenerator.Emit(OpCodes.Ldelem_Ref);
cloneIlGenerator.Emit(OpCodes.Stloc_S, sourceEntityReferenceEntityKeyMemberProp);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, valueEntityReferenceEntityKeysMemberProp);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, loopIndex);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeyMemberProp);
cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityKeyMember).GetMethod("get_Key"));
cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeyMemberProp);
cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityKeyMember).GetMethod("get_Value"));
cloneIlGenerator.Emit(OpCodes.Newobj, typeof(EntityKeyMember).GetConstructor(new Type[] { typeof(string), typeof(object) }));
cloneIlGenerator.Emit(OpCodes.Stelem_Ref);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, loopIndex);
cloneIlGenerator.Emit(OpCodes.Ldc_I4_1);
cloneIlGenerator.Emit(OpCodes.Add);
cloneIlGenerator.Emit(OpCodes.Stloc_S, loopIndex);
cloneIlGenerator.Emit(OpCodes.Br, startLoopLabel);
cloneIlGenerator.MarkLabel(endOfLoopLable);
cloneIlGenerator.Emit(OpCodes.Ldloc_S, sourceEntityReferenceEntityKeyProp);
cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityKey).GetMethod("get_EntityContainerName"));
cloneIlGenerator.Emit(OpCodes.Ldstr, ".");
cloneIlGenerator.Emit(OpCodes.Ldloc, sourceEntityReferenceEntityKeyProp);
cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(EntityKey).GetMethod("get_EntitySetName"));
cloneIlGenerator.Emit(OpCodes.Call, typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) })); // We suppose EntityContainerName and EntitySetName aren't null
cloneIlGenerator.Emit(OpCodes.Ldloc_S, valueEntityReferenceEntityKeysMemberProp);
cloneIlGenerator.Emit(OpCodes.Newobj, typeof(EntityKey).GetConstructor(new Type[] { typeof(string), typeof(IEnumerable<EntityKeyMember>) }));
var noEntityKeyValuesEnd = cloneIlGenerator.DefineLabel();
cloneIlGenerator.Emit(OpCodes.Br_S, noEntityKeyValuesEnd);
cloneIlGenerator.MarkLabel(noEntityKeyValues);
cloneIlGenerator.Emit(OpCodes.Newobj, typeof(EntityKey).GetConstructor(new Type[0]));
cloneIlGenerator.MarkLabel(noEntityKeyValuesEnd);
cloneIlGenerator.Emit(OpCodes.Callvirt, prop.PropertyType.GetMethod("set_EntityKey"));
if (copyNull)
{
var endCopyReferenceLabel = cloneIlGenerator.DefineLabel();
cloneIlGenerator.Emit(OpCodes.Br_S, endCopyReferenceLabel);
cloneIlGenerator.MarkLabel(entityKeyNullLabel);
cloneIlGenerator.Emit(OpCodes.Ldloc, valueEntityReferenceProp);
cloneIlGenerator.Emit(OpCodes.Ldnull);
cloneIlGenerator.Emit(OpCodes.Callvirt, typeof(T).GetMethod("set_" + prop.Name));
cloneIlGenerator.MarkLabel(endCopyReferenceLabel);
}
else
cloneIlGenerator.MarkLabel(entityKeyNullLabel);
}
public static T Clone(T obj)
{
return CloneDelegate(obj);
}
}