private static object DeepClone(object srcObject, Dictionary <object, object> visited, bool checkType = true) { object existing; if (visited.TryGetValue(srcObject, out existing)) { return(existing); } var srcType = srcObject.GetType(); // FOR DEEP TREES OR COLLECTIONS OF SAME TYPE if (null == lastTypeUsed || lastTypeUsed.Type != srcType) { lastTypeUsed = GetTypeInfos(srcType); } var typeCache = lastTypeUsed ?? GetTypeInfos(srcObject.GetType()); return(typeCache.CopyMethod(srcObject, visited)); }
private static TypeCahce GetTypeInfos(Type srcType) { TypeCahce result; if (!typeCache.TryGetValue(srcType, out result)) { result = new TypeCahce() { Type = srcType, IsValueType = srcType.IsValueType }; ParameterExpression inputExpr = Expression.Parameter(TYPE_OBJECT); var visitedParam = Expression.Parameter(TYPE_DICT_OBJ_OBJ); if (!srcType.IsValueType && !srcType.IsArray && srcType.GetConstructor(Type.EmptyTypes) == null) { result.CopyMethod = Expression.Lambda <Func <object, Dictionary <object, object>, object> >( Expression.Constant(null, srcType), inputExpr, visitedParam).Compile(); typeCache.Add(srcType, result); return(result); } var memberwiseCloneExpr = Expression.Call(inputExpr, memberwiseClone); var resultVar = Expression.Variable(srcType); var srcVar = Expression.Variable(srcType); var copyExpressions = new List <Expression>(); var copyVariables = new List <ParameterExpression>() { resultVar, srcVar }; copyExpressions.Add(Expression.Assign(srcVar, Expression.Convert(inputExpr, srcType))); copyExpressions.Add(Expression.Assign(resultVar, Expression.Convert(memberwiseCloneExpr, srcType))); //copyExpressions.Add(Expression.Assign(resultVar, Expression.Convert(Expression.New(srcType), srcType))); if (srcType.IsArray) { var arrCopyExpr = Expression.Assign(resultVar, Expression.Convert(Expression.Call(thisArrayClone, Expression.Convert(srcVar, TYPE_ARRAY), visitedParam), srcType)); copyExpressions.Add(arrCopyExpr); } else if (TYPE_ICOLLECTION.IsAssignableFrom(srcType) && srcType.IsGenericType) { var listCopyExpr = Expression.Assign(resultVar, Expression.Convert(Expression.Call(thisCollectionClone, Expression.Convert(srcVar, TYPE_ICOLLECTION), visitedParam), srcType)); copyExpressions.Add(listCopyExpr); } else { copyExpressions.Add(Expression.Assign(Expression.Property(visitedParam, TYPE_DICT_OBJ_OBJ.GetProperty("Item"), inputExpr), Expression.Convert(resultVar, TYPE_OBJECT))); BuildPropertiesExpression(result, srcType, srcVar, resultVar, visitedParam, copyExpressions); } copyExpressions.Add(Expression.Convert(resultVar, TYPE_OBJECT)); var copyExpr = Expression.Lambda <Func <object, Dictionary <object, object>, object> >( Expression.Block(copyVariables, copyExpressions), inputExpr, visitedParam).Compile(); result.CopyMethod = copyExpr; typeCache.Add(srcType, result); return(result); } return(result); }
private static void BuildPropertiesExpression(TypeCahce result, Type srcType, ParameterExpression srcVar, ParameterExpression resultVar, ParameterExpression visitedParam, List <Expression> copyExpressions ) { foreach (var eachField in srcType.GetFields(BindingFlags.Public | BindingFlags.Instance)) { var mode = GetAttrMode(eachField); if (mode != CloningMode.Shallow) { var fldCopyTo = Expression.Field(resultVar, eachField); if (mode == CloningMode.Deep) { if (!IsPrimitive(eachField.FieldType)) { bool needtToCheck = NeedToCheckType(eachField.FieldType); var fldCopyFrom = Expression.Field(srcVar, eachField); var chekType = Expression.Constant(needtToCheck, TYPE_BOOL); var fldCopyExpr = Expression.Assign(fldCopyTo, Expression.Convert(Expression.Call(thisClone, Expression.Convert(fldCopyFrom, TYPE_OBJECT), visitedParam, chekType), eachField.FieldType)); if (eachField.FieldType.IsValueType) { copyExpressions.Add(fldCopyExpr); } else { var copyIfNotNull = Expression.IfThen(Expression.NotEqual(fldCopyFrom, Expression.Constant(null, eachField.FieldType)), fldCopyExpr); if (needtToCheck) { // todo: add more type checks copyExpressions.Add(Expression.IfThen(Expression.Not(Expression.TypeEqual(fldCopyFrom, TYPE_INT)), copyIfNotNull)); } else { copyExpressions.Add(copyIfNotNull); } } } } else //if (mode == CloningMode.Ignore) { copyExpressions.Add(Expression.Assign(fldCopyTo, Expression.Default(eachField.FieldType))); } } } foreach (var eachProp in srcType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (!eachProp.CanWrite) { continue; } var mode = GetAttrMode(eachProp); if (mode != CloningMode.Shallow) { var propCopyTo = Expression.Property(resultVar, eachProp); if (mode == CloningMode.Deep) { if (!IsPrimitive(eachProp.PropertyType)) { bool needtToCheck = NeedToCheckType(eachProp.PropertyType); var chekType = Expression.Constant(needtToCheck, TYPE_BOOL); var propCopyFrom = Expression.Property(srcVar, eachProp); var propCopyExpr = Expression.Assign(propCopyTo, Expression.Convert( Expression.Call(thisClone, Expression.Convert(propCopyFrom, TYPE_OBJECT), visitedParam, chekType), eachProp.PropertyType)); if (eachProp.PropertyType.IsValueType) { copyExpressions.Add(propCopyExpr); } else { var copyIfNotNUllExpr = Expression.IfThen(Expression.NotEqual(propCopyFrom, Expression.Constant(null, eachProp.PropertyType)), propCopyExpr); if (needtToCheck) { // todo: add more type checks copyExpressions.Add(Expression.IfThen(Expression.Not(Expression.TypeEqual(propCopyFrom, TYPE_INT)), copyIfNotNUllExpr)); } else { copyExpressions.Add(copyIfNotNUllExpr); } } } } else //if (mode == CloningMode.Ignore) { copyExpressions.Add(Expression.Assign(propCopyTo, Expression.Default(eachProp.PropertyType))); } } } }