Ask a questionAsk a question
 

QuestionHow do I use Reflection to clone an object?

  • Wednesday, November 04, 2009 8:45 PMMichael Fitzpatrick Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Please forgive the formatting- the web form to add code really sucks and I did the best I can
    using System;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Collections;
    using System.Diagnostics;
    namespace CLONEOBJ_NS {
        public class CloneObj {
            private string m_ErrMsg;
            public object Clone(object src) {
                // http://gaaton.blogspot.com/2009/04/clone-object-in-c-using-reflection-for.html            object dst = Activator.CreateInstance(src.GetType());
                PropertyInfo[] propertyInfos = src.GetType().GetProperties();
                try {
                    foreach (PropertyInfo propertyInfo in propertyInfos) {
                        if (propertyInfo.CanWrite && propertyInfo.CanRead && propertyInfo.Name.ToLower() != "parent") {
                            try {
                                if (propertyInfo.PropertyType.IsGenericType) {
                                    if (propertyInfo.PropertyType.GetInterface("IList", true) != null) {
                                        IList oldList = propertyInfo.GetValue(src, null) as IList;
                                        if (oldList != null
                                            && oldList.Count > 0
    && oldList[0].GetType().GetInterface("ICloneable", true) != null) {
    IList newList = (IList)propertyInfo.GetValue(dst, null); foreach (object obj in oldList) { ICloneable clone = (ICloneable)obj; newList.Add(clone.Clone()); } } else { propertyInfo.SetValue(dst, oldList, null); } } if (propertyInfo.PropertyType.GetInterface("IDictionary", true) != null) { IDictionary oldDic = propertyInfo.GetValue(src, null) as IDictionary; if (oldDic != null && oldDic.Count > 0 && oldDic[0].GetType().GetInterface("ICloneable", true) != null) { IDictionary newDic = (IDictionary)propertyInfo.GetValue(dst, null); foreach (DictionaryEntry entry in oldDic) { ICloneable clone = (ICloneable)entry.Value; newDic[entry.Key] = clone.Clone(); } } else { propertyInfo.SetValue(dst, oldDic, null); } } } else { // if (propertyInfo.PropertyType.IsGenericType) //Clone IClonable object if (propertyInfo.GetType().GetInterface("ICloneable", true) != null) { ICloneable clone = (ICloneable)propertyInfo.GetValue(src, null); propertyInfo.SetValue(dst, clone.Clone(), null); } else { object o = propertyInfo.GetValue(src, null); propertyInfo.SetValue(dst, o, null); } } // else .. if (propertyInfo.PropertyType.IsGenericType) } catch (Exception ex) { Debug.Print("\n##########################\n" + ex.ToString() + "\n"); } } } } catch (Exception ex) { m_ErrMsg = ex.ToString(); Debug.Assert(false, ex.ToString()); } // http://msdn.microsoft.com/en-us/library/ms228976.aspx try { EventInfo[] EventInfos = src.GetType().GetEvents(); foreach (EventInfo eInfo in EventInfos) { // Get an EventInfo representing the Click event, and get the // type of delegate that handles the event. Type tSrc = eInfo.EventHandlerType; // If you already have a method with the correct signature, // you can simply get a MethodInfo for it. MethodInfo miHandler = tSrc.GetMethod("Invoke"); //eInfo.Name); //, // eInfo.Name, //BindingFlags.NonPublic | BindingFlags.Instance); if (miHandler != null) { // Create an instance of the delegate. Using the overloads // of CreateDelegate that take MethodInfo is recommended. Delegate d = Delegate.CreateDelegate(tSrc, src, "Invoke"); //miHandler); // Get the "add" accessor of the event and invoke it late- // bound, passing in the delegate instance. This is equivalent // to using the += operator in C#, or AddHandler in Visual // Basic. The instance on which the "add" accessor is invoked // is the form; the arguments must be passed as an array. MethodInfo addHandler = eInfo.GetAddMethod(); Object[] addHandlerArgs = { d }; addHandler.Invoke(dst, addHandlerArgs); } //Type handlerType = eInfo.EventHandlerType; //EventInfo eventHandler = handlerType.GetEvent(eInfo.Name); //MethodInfo mi = handlerType.GetMethod("Invoke", BindingFlags.NonPublic | BindingFlags.Instance); //if (eventHandler != null) { // MethodInfo invokeMethod = handlerType.GetMethod("Invoke"); // ParameterInfo[] parms = invokeMethod.GetParameters(); // Type[] parmTypes = new Type[parms.Length]; // Delegate d = Delegate.CreateDelegate(handlerType, mi); // MethodInfo addHandler = eventHandler.GetAddMethod("Invoke"); // for (int i = 0; i < parms.Length; i++) { // parmTypes[i] = parms[i].ParameterType; // } // //MethodInfo mi = eventHandler.GetRaiseMethod(); // if (mi != null) { // Delegate d = Delegate.CreateDelegate(handlerType, mi); // eInfo.AddEventHandler(dst, d); // } //} } } catch (Exception ex) { m_ErrMsg = ex.ToString(); Debug.Assert(false, ex.ToString()); } return dst; } // public object Clone(object src) } // public class CloneObj



    I have a tabpage on my form labeled "Geometry". The client has a new requirement that I have multiple geometries. My first attempt at retrofitting the the application was to use reflection to clone the tabpage and all the child controls on it. Here is the code I used (I tried to put it below but the UI won't let me!)

    Two problems here:
    1. I can't get the events to copy into the dst object.
    2. I had to remove the "Parent" property from the clone or else the controls ended up on the src tab.
    3. I still need to do the fields collection but I hope it is trivial.

    Here's a little VB code I use to call the C# above:


     

    Sub listctrl(ByVal ctrl As Control, ByVal n As Integer)

     

     

    If ctrl.Name <> "" Then

    ctrl.Visible =

    True

    ctrl.Refresh()

    Application.DoEvents() : CodeInsight.CI.MyDoEvents(1)

    g_CommIO.MsgLine(Space(n * 3) & ctrl.Name &

    ": " & ctrl.Visible.ToString)

     

     

    Else

    g_CommIO.MsgLine(Space(n * 3) &

    "#" & ctrl.ToString & ": " & ctrl.Visible.ToString)

     

     

    End If

     

    For Each c As Control In ctrl.Controls

     

    Next

     

    End Sub

     

    Function Clone(ByVal src As Control _

     

     

    ByRef dst As Control _

     

     

    ByVal oCln As ZedGraph.CloneObj _

     

     

    ByVal n As Integer) As Control

     

     

    Try

    dst = oCln.Clone(src)

    dst.Visible =

    True 'srcCtrl.Visible

     

    'dst.Name &= n

     

    Dim i As Integer = 0

     

     

    For Each srcCtrl As Control In src.Controls

     

     

    Dim dstCtrl As Control = Nothing

    dstCtrl = Clone(srcCtrl, dstCtrl, oCln, n)

    dst.Controls.Add(dstCtrl)

    Application.DoEvents() : CodeInsight.CI.MyDoEvents(1)

    i += 1

     

    Next

    dst.Refresh()

    Application.DoEvents() : CodeInsight.CI.MyDoEvents()

     

    Return dst

     

     

    Catch ex As Exception

     

     

    If mnuDebugMsg.Checked Then g_CommIO.MsgLine(g_ErrMsg)

     

     

    False, ex.ToString)

     

     

    End Try

     

    Return Nothing

     

    End Function

     

    Private Sub mnuGeometryNewTab_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _

     

     

    Handles mnuGeometryAddTab.Click

     

     

    Dim oCln As New ZedGraph.CloneObj()

     

     

    Dim dstTpg As TabPage = Nothing

    dstTpg = Clone(tpgGeometry, dstTpg, oCln, 1)

    Application.DoEvents() : CodeInsight.CI.MyDoEvents()

    listctrl(dstTpg, 1)

    tabAnalysis.Controls.Add(dstTpg)

     

    End Sub

     

     

     

     

    Debug.Assert(

    g_ErrMsg = ex.ToString :

     

     

     

     

    ,

    ,

    ,

    listctrl(c, n + 1)