Note that there are some explanatory texts on larger screens.

plurals
  1. POWhen Deep Copying a WPF Data Bound Object
    primarykey
    data
    text
    <p>I wrote a nifty method to deep copy any object. It does so by recursively calling <code>MemberwiseClone()</code> on any reference type field inside the instance. This method works perfectly on any object I care to use it, including hierarchy relationship objects. The method also sports a dictionary of past visitations so unnecessary duplicate work is avoided. </p> <p>The problem I am having, however, is that this method only works when the object is NOT data bound to WPF/MVVM when a clone is needed. When data bound and the method is invoked, I run into stack overflow exceptions because of (I assume) an established link between <code>INotifyPropertyChanged.PropertyChanged</code> event and the WPF framework. The recursive call then attempts to copy the entire universe of objects including the <code>AppDomain</code> and low-level Pointer objects, which appear to be linked and go on to almost infinity (more than VS2012 can handle, anyway.)</p> <p>I doubt I will ever have need to deep copy an object graph that goes back to the beginning of the <code>AppDomain</code>... is there a smart way to have my copy method "stop" when it reaches a certain boundary? I also thought about simply copying objects before they're data bound, but I'm not sure that's a feasible option, and it's rather silly. I just want a simple deep copy solution that works on types that are not serializable, but are also data-bound via <code>INotifyPropertyChanged</code>.</p> <p>The method's implementation:</p> <pre><code>private static object Clone(object instance, IDictionary&lt;object, object&gt; visitGraph) { var instanceType = instance.GetType(); Debug.WriteLine(instanceType.Name); object clonedInstance = null; if (visitGraph.ContainsKey(instance)) { clonedInstance = visitGraph[instance]; } else { const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var memberwiseCloneMethod = instanceType.GetMethods(flags).Single(x =&gt; x.Name == "MemberwiseClone" &amp;&amp; !x.GetParameters().Any()); clonedInstance = memberwiseCloneMethod.Invoke(instance, null); visitGraph.Add(instance, clonedInstance); var allReferenceTypeProperties = clonedInstance.GetType().GetAllFields() .Where( x =&gt; !x.FieldType.IsValueType &amp;&amp; x.FieldType != typeof (string)); foreach (var field in allReferenceTypeProperties) { var existingFieldValue = field.GetValue(instance); if (existingFieldValue != null) { var clonedFieldValue = Clone(existingFieldValue, visitGraph); field.SetValue(clonedInstance, clonedFieldValue); } } } return clonedInstance; } public static IEnumerable&lt;FieldInfo&gt; GetAllFields(this Type type) { const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var fields = type.GetFields(flags); foreach (var field in fields) { yield return field; } if (type.BaseType != null) { foreach (var field in GetAllFields(type.BaseType)) { yield return field; } } } public static object Copy(this object instance) { if (instance == null) throw new ArgumentNullException("instance"); var visitGraph = new Dictionary&lt;object, object&gt;(); var clonedInstance = Clone(instance, visitGraph); return clonedInstance; } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload