public static T CloneObject <T>(T obj) { // this is faster than typeof(T).IsValueType if (obj is ValueType) { if (typeof(T) == obj.GetType()) { return(obj); } // we're here so, we clone value type obj as object type T // so, we need to copy it, bcs we have a reference, not real object. return((T)ShallowObjectCloner.CloneObject(obj)); } if (ReferenceEquals(obj, null)) { return((T)(object)null); } if (DeepClonerSafeTypes.CanReturnSameObject(obj.GetType())) { return(obj); } return((T)ShallowObjectCloner.CloneObject(obj)); }
internal static T[,] Clone2DimArrayInternal <T>(T[,] objFrom, T[,] objTo, DeepCloneState state, bool isDeep) { // not null from called method, but will check it anyway if (objFrom == null || objTo == null) { return(null); } var l1 = Math.Min(objFrom.GetLength(0), objTo.GetLength(0)); var l2 = Math.Min(objFrom.GetLength(1), objTo.GetLength(1)); state.AddKnownRef(objFrom, objTo); if ((!isDeep || DeepClonerSafeTypes.CanReturnSameObject(typeof(T))) && objFrom.GetLength(0) == objTo.GetLength(0) && objFrom.GetLength(1) == objTo.GetLength(1)) { Array.Copy(objFrom, objTo, objFrom.Length); return(objTo); } if (!isDeep) { for (var i = 0; i < l1; i++) { for (var k = 0; k < l2; k++) { objTo[i, k] = objFrom[i, k]; } } return(objTo); } if (typeof(T).IsValueType()) { var cloner = DeepClonerGenerator.GetClonerForValueType <T>(); for (var i = 0; i < l1; i++) { for (var k = 0; k < l2; k++) { objTo[i, k] = cloner(objFrom[i, k], state); } } } else { for (var i = 0; i < l1; i++) { for (var k = 0; k < l2; k++) { objTo[i, k] = (T)DeepClonerGenerator.CloneClassInternal(objFrom[i, k], state); } } } return(objTo); }
private static object GenerateProcessArrayMethod(Type type, bool isDeep) { var elementType = type.GetElementType(); var rank = type.GetArrayRank(); ParameterExpression from = Expression.Parameter(typeof(object)); ParameterExpression to = Expression.Parameter(typeof(object)); var state = Expression.Parameter(typeof(DeepCloneState)); var funcType = typeof(Func <, , ,>).MakeGenericType(typeof(object), typeof(object), typeof(DeepCloneState), typeof(object)); if (rank == 1 && type == elementType.MakeArrayType()) { if (!isDeep) { var callS = Expression.Call( typeof(ClonerToExprGenerator).GetPrivateStaticMethod("ShallowClone1DimArraySafeInternal") .MakeGenericMethod(elementType), Expression.Convert(from, type), Expression.Convert(to, type)); return(Expression.Lambda(funcType, callS, from, to, state).Compile()); } else { var methodName = "Clone1DimArrayClassInternal"; if (DeepClonerSafeTypes.CanReturnSameObject(elementType)) { methodName = "Clone1DimArraySafeInternal"; } else if (elementType.IsValueType()) { methodName = "Clone1DimArrayStructInternal"; } var methodInfo = typeof(ClonerToExprGenerator).GetPrivateStaticMethod(methodName).MakeGenericMethod(elementType); var callS = Expression.Call(methodInfo, Expression.Convert(from, type), Expression.Convert(to, type), state); return(Expression.Lambda(funcType, callS, from, to, state).Compile()); } } else { // multidim or not zero-based arrays var methodInfo = typeof(ClonerToExprGenerator).GetPrivateStaticMethod( rank == 2 && type == elementType.MakeArrayType() ? "Clone2DimArrayInternal" : "CloneAbstractArrayInternal"); var callS = Expression.Call(methodInfo, Expression.Convert(from, type), Expression.Convert(to, type), state, Expression.Constant(isDeep)); return(Expression.Lambda(funcType, callS, from, to, state).Compile()); } }
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 GenerateProcessArrayMethod(Type type) { var elementType = type.GetElementType(); var rank = type.GetArrayRank(); MethodInfo methodInfo; // multidim or not zero-based arrays if (rank != 1 || type != elementType.MakeArrayType()) { if (rank == 2 && type == elementType.MakeArrayType()) { // small optimization for 2 dim arrays methodInfo = typeof(DeepClonerGenerator).GetPrivateStaticMethod("Clone2DimArrayInternal").MakeGenericMethod(elementType); } else { methodInfo = typeof(DeepClonerGenerator).GetPrivateStaticMethod("CloneAbstractArrayInternal"); } } else { var methodName = "Clone1DimArrayClassInternal"; if (DeepClonerSafeTypes.CanReturnSameObject(elementType)) { methodName = "Clone1DimArraySafeInternal"; } else if (elementType.IsValueType()) { methodName = "Clone1DimArrayStructInternal"; } methodInfo = typeof(DeepClonerGenerator).GetPrivateStaticMethod(methodName).MakeGenericMethod(elementType); } ParameterExpression from = Expression.Parameter(typeof(object)); var state = Expression.Parameter(typeof(DeepCloneState)); var call = Expression.Call(methodInfo, Expression.Convert(from, type), state); var funcType = typeof(Func <, ,>).MakeGenericType(typeof(object), typeof(DeepCloneState), typeof(object)); return(Expression.Lambda(funcType, call, from, state).Compile()); }
// 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).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); }
private static object GenerateCloner(Type t, bool asObject) { if (DeepClonerSafeTypes.CanReturnSameObject(t) && (asObject && !t.IsValueType())) { return(null); } #if !NETCORE if (ShallowObjectCloner.IsSafeVariant()) { return(DeepClonerExprGenerator.GenerateClonerInternal(t, asObject)); } else { return(DeepClonerMsilGenerator.GenerateClonerInternal(t, asObject)); } #else return(DeepClonerExprGenerator.GenerateClonerInternal(t, asObject)); #endif }
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.GenericArguments(); // 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 !NETCORE // don't do anything with this dark magic! if (tp == typeof(ContextBoundObject)) { break; } #else if (tp.Name == "ContextBoundObject") { break; } #endif fi.AddRange(tp.GetDeclaredFields()); 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()); }
private static object GenerateProcessMethod(Type type, bool isDeepClone) { if (type.IsArray) { return(GenerateProcessArrayMethod(type, isDeepClone)); } var methodType = typeof(object); var expressionList = new List <Expression>(); ParameterExpression from = Expression.Parameter(methodType); var fromLocal = from; var to = Expression.Parameter(methodType); var toLocal = to; var state = Expression.Parameter(typeof(DeepCloneState)); // if (!type.IsValueType()) { fromLocal = Expression.Variable(type); toLocal = Expression.Variable(type); // fromLocal = (T)from expressionList.Add(Expression.Assign(fromLocal, Expression.Convert(from, type))); expressionList.Add(Expression.Assign(toLocal, Expression.Convert(to, type))); if (isDeepClone) { // 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, to)); } } List <FieldInfo> fi = new List <FieldInfo>(); var tp = type; do { #if !NETCORE // don't do anything with this dark magic! if (tp == typeof(ContextBoundObject)) { break; } #else if (tp.Name == "ContextBoundObject") { break; } #endif fi.AddRange(tp.GetDeclaredFields()); tp = tp.BaseType(); }while (tp != null); foreach (var fieldInfo in fi) { if (isDeepClone && !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)); } } else { expressionList.Add(Expression.Assign(Expression.Field(toLocal, fieldInfo), Expression.Field(fromLocal, fieldInfo))); } } expressionList.Add(Expression.Convert(toLocal, methodType)); var funcType = typeof(Func <, , ,>).MakeGenericType(methodType, methodType, typeof(DeepCloneState), methodType); var blockParams = new List <ParameterExpression>(); if (from != fromLocal) { blockParams.Add(fromLocal); } if (to != toLocal) { blockParams.Add(toLocal); } return(Expression.Lambda(funcType, Expression.Block(blockParams, expressionList), from, to, state).Compile()); }