If your entire object graph is serialisable, my experience up to now
is that deep cloning is much easier and maintainable using in-memory
binary serialisation.

public static object DeepClone(object obj)
{
  if (obj == null)
    return null;

  BinaryFormatter bf = new BinaryFormatter();

  using (MemoryStream ms = new MemoryStream())
  {
    bf.Serialize(ms, obj);
    ms.Position = 0;

    return bf.Deserialize(ms);
  }
}

Simple unit test:

[Test()]
public void DeepCloneTest()
{
  Assert.IsNull(PC.Utilities.DeepClone(null));

  DummyCloneable data = new DummyCloneable();
  DummyCloneable clone = (DummyCloneable) PC.Utilities.DeepClone(data);

  Assert.IsTrue(data.CheckDeepClone(clone));
}

[Serializable()]
private class DummyCloneable
{
  public int Data1;
  public string Data2;
  public object Data3;
  public List<int> Data4;
  public List<List<int>> Data5;
  public Dictionary<string, object> Data6;

  public DummyCloneable()
  {
    Data1 = 123;
    Data2 = "foo";
    Data3 = new object();
    Data4 = new List<int>(new int[] {1, 2, 3, 4, 5});

    Data5 = new List<List<int>>();
    Data5.Add(new List<int>(new int[] { 1, 2, 3, 4, 5 }));
    Data5.Add(new List<int>(new int[] { 1, 2, 3, 4, 5 }));
    Data5.Add(new List<int>(new int[] { 1, 2, 3, 4, 5 }));

    Data6 = new Dictionary<string, object>();
    Data6["key1"] = "value1";
    Data6["key2"] = 123;
    Data6["key3"] = 123.456D;
    Data6["key4"] = new object();
    Data6["key5"] = Data5;
  }

  public bool CheckDeepClone(DummyCloneable obj)
  {
    if (obj.Data1 != this.Data1)
      return false;

    if (obj.Data2 != this.Data2)
      return false;

    if (obj.Data3.Equals(this.Data3))
      return false;

    if (obj.Data4.Equals(this.Data4))
      return false;

    for (int i = 0; i < this.Data4.Count; i++)
      if (obj.Data4[i] != this.Data4[i])
        return false;

    if (obj.Data5.Equals(this.Data5))
      return false;

    for (int i = 0; i < this.Data5.Count; i++)
    {
      if (obj.Data5[i].Equals(this.Data5[i]))
        return false;

      for (int j = 0; j < this.Data5[i].Count; j++)
        if (obj.Data5[i][j] != this.Data5[i][j])
          return false;
    }

    if (obj.Data6.Equals(this.Data6))
      return false;

    if (((string) obj.Data6["key1"]) != ((string) this.Data6["key1"]))
      return false;

    if (((int) obj.Data6["key2"]) != ((int) this.Data6["key2"]))
      return false;

    if (((double) obj.Data6["key3"]) != ((double) this.Data6["key3"]))
      return false;

    if (obj.Data6["key4"].Equals(this.Data6["key4"]))
      return false;

    List<List<int>> objValue5 = (List<List<int>>) obj.Data6["key5"];
    List<List<int>> thisValue5 = (List<List<int>>) this.Data6["key5"];

    if (objValue5.Equals(thisValue5))
      return false;

    if (!objValue5.Equals(obj.Data5))
      return false;

    for (int i = 0; i < thisValue5.Count; i++)
    {
      if (objValue5[i].Equals(thisValue5[i]))
        return false;

      for (int j = 0; j < thisValue5[i].Count; j++)
        if (objValue5[i][j] != thisValue5[i][j])
          return false;
    }

    return true;
  }
}

On 10/30/07, David Nicholson <[EMAIL PROTECTED]> wrote:
> No I hadn't thought of it. I haven't used serialisation very much, so I'm
> a little wary about what might go on under the covers. Perhaps I shouldn't
> be.
>
> I take care to follow a process, i.e. add a member, add an initialiser and
> property if needed, add to Clone(). But as the class grows it would be
> nice to have an automated check that these things have been done. Test
> first doesn't work because you can't compile an initial failing test.
>
> Clone stands out because the effect of omitting a member is often subtle,
> whereas a missing property is obvious immediately.
>
> Thanks
> David.
>
>
>
> On Tue, 30 Oct 2007 16:03:38 +0800, =?ISO-8859-1?Q?S=E9bastien_Lorion?=
> <[EMAIL PROTECTED]> wrote:
>
> >Have you thought about using serialisation for deep cloning (using
> >MemoryStream) ?
> >
> >Sébastien
> >
> >On 10/29/07, David Nicholson <[EMAIL PROTECTED]> wrote:
> >> I have some classes where I provide a Clone() method to provide a deep
> >> copy of an instance, and would like to test that I haven't added a
> member
> >> to the class without also adding it to Clone().
> >>
> >> To test Clone() I set a non-default value for the member, then check
> that
> >> the cloned copy has that value. However if I forget to add it to Clone
> (),
> >> I'll probably also forget to add it to the test.
> >>
> >> Does anyone have a way to do this, other than the obvious one of being
> >> more disciplined?
> >>
> >> Thanks
> >> David.
> >>
> >> ===================================
> >> This list is hosted by DevelopMentor(R)  http://www.develop.com
> >>
> >> View archives and manage your subscription(s) at
> http://discuss.develop.com
> >>
> >
> >
> >--
> >Sébastien
> >www.sebastienlorion.com
>
> ===================================
> This list is hosted by DevelopMentor(R)  http://www.develop.com
>
> View archives and manage your subscription(s) at http://discuss.develop.com
>


-- 
Sébastien
www.sebastienlorion.com

===================================
This list is hosted by DevelopMentor®  http://www.develop.com

View archives and manage your subscription(s) at http://discuss.develop.com

Reply via email to