If you use binary serialization to clone, you're halfway to copy/paste, if that's a goal. If a class is properly clonable, it ought to be serializable/deserializable, it seems to me. Then you can kill two birds with one stone: implement Iclonable in terms of binary serialization. If a class derives from MarshalByRefObject, it probably shouldn't be clonable, but I don't want to be dogmatic about it.
-----Original Message----- From: Discussion of advanced .NET topics. [mailto:[EMAIL PROTECTED] On Behalf Of Sébastien Lorion Sent: Wednesday, October 31, 2007 4:28 AM To: ADVANCED-DOTNET@DISCUSS.DEVELOP.COM Subject: Re: [ADVANCED-DOTNET] Unit testing of Clone() 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 =================================== This list is hosted by DevelopMentor® http://www.develop.com View archives and manage your subscription(s) at http://discuss.develop.com