How do I use Reflection to clone an object?
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)
- Edited byMichael Fitzpatrick Wednesday, November 04, 2009 8:48 PMUI sucks on this editor


