Search code examples
c#reflectionclone

C# reflection, cloning


Say I have this class Myclass that contains this method:

 public class MyClass
    {
        public int MyProperty { get; set; }

        public int MySecondProperty { get; set; }

        public MyOtherClass subClass { get; set; }

        public object clone<T>(object original, T emptyObj)
        {

            FieldInfo[] fis = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);


            object tempMyClass = Activator.CreateInstance(typeof(T));


            foreach (FieldInfo fi in fis)
            {
                if (fi.FieldType.Namespace != original.GetType().Namespace)
                    fi.SetValue(tempMyClass, fi.GetValue(original));
                else
                    fi.SetValue(tempMyClass, this.clone(fi.GetValue(original), fi.GetValue(original)));
            }

            return tempMyClass;
        }
}

Then this class:

public class MyOtherClass 
{
    public int MyProperty777 { get; set; }
}

when I do this:

MyClass a = new MyClass { 
                        MyProperty = 1, 
                        MySecondProperty = 2, 
                        subClass = new MyOtherClass() { MyProperty777 = -1 } 
                        };
            MyClass b = a.clone(a, a) as MyClass;

how come on the second call to clone, T is of type object and not of type MyOtherClass


Solution

  • Your second (recursive) call to clone passes the result of GetValue as the second argument, which is of type object, and hence T is object.

    i.e.

    fi.SetValue(tempMyClass, this.clone(fi.GetValue(original), fi.GetValue(original)));
    

    The result of GetValue on a FieldInfo is an object.

    Given that you pass the same thing twice in all cases, the design of the clone method is possibly wrong. You probably don't need generics there. Just use obj.GetType() to get the type information of the second argument (if indeed you really need a second argument).

    It would make more sense to constrain the return type using generics, so that the cast isn't necessary on the calling side. Also you could make Clone into an extension method so it could apply to anything.

    On the other hand, the thing you're trying to do (an automatic deep clone) is unlikely to be generally useful. Most classes end up hold references to things that they don't own, so if you clone such an object, you end up accidentally cloning half of your application framework.