private static object GenerateCloner(Type t, bool asObject) { if (DeepClonerSafeTypes.CanReturnSameObject(t) && (asObject && !t.IsValueType)) { return(null); } return(DeepClonerExprGenerator.GenerateClonerInternal(t, asObject)); }
// relatively frequent case. specially handled internal static T[,] Clone2DimArrayInternal <T>(T[,] obj, DeepCloneState state) { // not null from called method, but will check it anyway if (obj == null) { return(null); } var l1 = obj.GetLength(0); var l2 = obj.GetLength(1); var outArray = new T[l1, l2]; state.AddKnownRef(obj, outArray); if (DeepClonerSafeTypes.CanReturnSameObject(typeof(T))) { Array.Copy(obj, outArray, obj.Length); return(outArray); } if (typeof(T).GetTypeInfo().IsValueType) { var cloner = GetClonerForValueType <T>(); for (var i = 0; i < l1; i++) { for (var k = 0; k < l2; k++) { outArray[i, k] = cloner(obj[i, k], state); } } } else { for (var i = 0; i < l1; i++) { for (var k = 0; k < l2; k++) { outArray[i, k] = (T)CloneClassInternal(obj[i, k], state); } } } return(outArray); }
public static T CloneObject <T>(T obj) { if (obj is ValueType) { var type = obj.GetType(); if (typeof(T) == type) { if (DeepClonerSafeTypes.CanReturnSameObject(type)) { return(obj); } return(CloneStructInternal(obj, new DeepCloneState())); } } return((T)CloneClassRoot(obj)); }
private static object GenerateProcessMethod(Type type, bool unboxStruct) { if (type.IsArray) { return(GenerateProcessArrayMethod(type)); } if (type.FullName != null && type.FullName.StartsWith("System.Tuple`")) { // if not safe type it is no guarantee that some type will contain reference to // this tuple. In usual way, we're creating new object, setting reference for it // and filling data. For tuple, we will fill data before creating object // (in constructor arguments) var genericArguments = type.GetGenericArguments(); // current tuples contain only 8 arguments, but may be in future... // we'll write code that works with it if (genericArguments.Length < 10 && genericArguments.All(DeepClonerSafeTypes.CanReturnSameObject)) { return(GenerateProcessTupleMethod(type)); } } var methodType = unboxStruct || type.IsClass ? typeof(object) : type; var expressionList = new List <Expression>(); ParameterExpression from = Expression.Parameter(methodType); var fromLocal = from; var toLocal = Expression.Variable(type); var state = Expression.Parameter(typeof(DeepCloneState)); if (!type.IsValueType) { var methodInfo = typeof(object).GetPrivateMethod("MemberwiseClone"); // to = (T)from.MemberwiseClone() expressionList.Add(Expression.Assign(toLocal, Expression.Convert(Expression.Call(from, methodInfo), type))); fromLocal = Expression.Variable(type); // fromLocal = (T)from expressionList.Add(Expression.Assign(fromLocal, Expression.Convert(from, type))); // added from -> to binding to ensure reference loop handling // structs cannot loop here // state.AddKnownRef(from, to) expressionList.Add(Expression.Call(state, typeof(DeepCloneState).GetMethod("AddKnownRef"), from, toLocal)); } else { if (unboxStruct) { // toLocal = (T)from; expressionList.Add(Expression.Assign(toLocal, Expression.Unbox(from, type))); fromLocal = Expression.Variable(type); // fromLocal = toLocal; // structs, it is ok to copy expressionList.Add(Expression.Assign(fromLocal, toLocal)); } else { // toLocal = from expressionList.Add(Expression.Assign(toLocal, from)); } } List <FieldInfo> fi = new List <FieldInfo>(); var tp = type; do { if (tp.Name == "ContextBoundObject") { break; } fi.AddRange(tp.GetTypeInfo().DeclaredFields.Where(x => !x.IsStatic).ToArray()); tp = tp.BaseType; }while (tp != null); foreach (var fieldInfo in fi) { if (!DeepClonerSafeTypes.CanReturnSameObject(fieldInfo.FieldType)) { var methodInfo = fieldInfo.FieldType.IsValueType ? typeof(DeepClonerGenerator).GetPrivateStaticMethod("CloneStructInternal") .MakeGenericMethod(fieldInfo.FieldType) : typeof(DeepClonerGenerator).GetPrivateStaticMethod("CloneClassInternal"); var get = Expression.Field(fromLocal, fieldInfo); // toLocal.Field = Clone...Internal(fromLocal.Field) var call = (Expression)Expression.Call(methodInfo, get, state); if (!fieldInfo.FieldType.IsValueType) { call = Expression.Convert(call, fieldInfo.FieldType); } // should handle specially // todo: think about optimization, but it rare case if (fieldInfo.IsInitOnly) { // var setMethod = fieldInfo.GetType().GetMethod("SetValue", new[] { typeof(object), typeof(object) }); // expressionList.Add(Expression.Call(Expression.Constant(fieldInfo), setMethod, toLocal, call)); var setMethod = typeof(DeepClonerExprGenerator).GetPrivateStaticMethod("ForceSetField"); expressionList.Add(Expression.Call(setMethod, Expression.Constant(fieldInfo), Expression.Convert(toLocal, typeof(object)), Expression.Convert(call, typeof(object)))); } else { expressionList.Add(Expression.Assign(Expression.Field(toLocal, fieldInfo), call)); } } } expressionList.Add(Expression.Convert(toLocal, methodType)); var funcType = typeof(Func <, ,>).MakeGenericType(methodType, typeof(DeepCloneState), methodType); var blockParams = new List <ParameterExpression>(); if (from != fromLocal) { blockParams.Add(fromLocal); } blockParams.Add(toLocal); return(Expression.Lambda(funcType, Expression.Block(blockParams, expressionList), from, state).Compile()); }