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());
        }