diff --git a/Nuclex.Support (net-4.0).csproj b/Nuclex.Support (net-4.0).csproj
index 5303e18..70420ef 100644
--- a/Nuclex.Support (net-4.0).csproj
+++ b/Nuclex.Support (net-4.0).csproj
@@ -60,6 +60,7 @@
AffineThreadPool.cs
+
@@ -68,6 +69,10 @@
ReflectionCloner.cs
+
+
+ SerializationCloner.cs
+
Deque.cs
diff --git a/Nuclex.Support (xna-4.0-phone7).csproj b/Nuclex.Support (xna-4.0-phone7).csproj
index 0dc521e..698f1b1 100644
--- a/Nuclex.Support (xna-4.0-phone7).csproj
+++ b/Nuclex.Support (xna-4.0-phone7).csproj
@@ -91,6 +91,7 @@
AffineThreadPool.cs
+
@@ -99,6 +100,10 @@
ReflectionCloner.cs
+
+
+ SerializationCloner.cs
+
Deque.cs
diff --git a/Nuclex.Support (xna-4.0-xbox360).csproj b/Nuclex.Support (xna-4.0-xbox360).csproj
index 63c0c4c..7b32470 100644
--- a/Nuclex.Support (xna-4.0-xbox360).csproj
+++ b/Nuclex.Support (xna-4.0-xbox360).csproj
@@ -102,6 +102,7 @@
AffineThreadPool.cs
+
@@ -110,6 +111,10 @@
ReflectionCloner.cs
+
+
+ SerializationCloner.cs
+
Deque.cs
diff --git a/Source/Cloning/CloneFactoryTest.cs b/Source/Cloning/CloneFactoryTest.cs
new file mode 100644
index 0000000..4991008
--- /dev/null
+++ b/Source/Cloning/CloneFactoryTest.cs
@@ -0,0 +1,446 @@
+#region CPL License
+/*
+Nuclex Framework
+Copyright (C) 2002-2010 Nuclex Development Labs
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the IBM Common Public License as
+published by the IBM Corporation; either version 1.0 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+IBM Common Public License for more details.
+
+You should have received a copy of the IBM Common Public
+License along with this library
+*/
+#endregion
+
+#if UNITTEST
+
+using System;
+
+using NUnit.Framework;
+
+namespace Nuclex.Support.Cloning {
+
+ /// Base class for unit tests verifying the clone factory
+ public abstract class CloneFactoryTest {
+
+ #region class TestReferenceType
+
+ /// A reference type being used for testing
+ protected class TestReferenceType {
+
+ /// Field holding an integer value for testing
+ public int TestField;
+ /// Property holding an integer value for testing
+ public int TestProperty { get; set; }
+
+ }
+
+ #endregion // class TestReferenceType
+
+ #region struct TestValueType
+
+ /// A value type being used for testing
+ protected struct TestValueType {
+
+ /// Field holding an integer value for testing
+ public int TestField;
+ /// Property holding an integer value for testing
+ public int TestProperty { get; set; }
+
+ }
+
+ #endregion // struct TestValueType
+
+ #region struct HierarchicalValueType
+
+ /// A value type containiner other complex types used for testing
+ protected struct HierarchicalValueType {
+
+ /// Field holding an integer value for testing
+ public int TestField;
+ /// Property holding an integer value for testing
+ public int TestProperty { get; set; }
+ /// Value type field for testing
+ public TestValueType ValueTypeField;
+ /// Value type property for testing
+ public TestValueType ValueTypeProperty { get; set; }
+ /// Reference type field for testing
+ public TestReferenceType ReferenceTypeField;
+ /// Reference type property for testing
+ public TestReferenceType ReferenceTypeProperty { get; set; }
+ /// An array field of reference types
+ public TestReferenceType[,][] ReferenceTypeArrayField;
+ /// An array property of reference types
+ public TestReferenceType[,][] ReferenceTypeArrayProperty { get; set; }
+
+ }
+
+ #endregion // struct HierarchicalValueType
+
+ #region struct HierarchicalReferenceType
+
+ /// A value type containiner other complex types used for testing
+ protected class HierarchicalReferenceType {
+
+ /// Field holding an integer value for testing
+ public int TestField;
+ /// Property holding an integer value for testing
+ public int TestProperty { get; set; }
+ /// Value type field for testing
+ public TestValueType ValueTypeField;
+ /// Value type property for testing
+ public TestValueType ValueTypeProperty { get; set; }
+ /// Reference type field for testing
+ public TestReferenceType ReferenceTypeField;
+ /// Reference type property for testing
+ public TestReferenceType ReferenceTypeProperty { get; set; }
+ /// An array field of reference types
+ public TestReferenceType[,][] ReferenceTypeArrayField;
+ /// An array property of reference types
+ public TestReferenceType[,][] ReferenceTypeArrayProperty { get; set; }
+
+ }
+
+ #endregion // struct HierarchicalReferenceType
+
+ ///
+ /// Verifies that a cloned object exhibits the expected state for the type of
+ /// clone that has been performed
+ ///
+ /// Original instance the clone was created from
+ /// Cloned instance that will be checked for correctness
+ /// Whether the cloned instance is a deep clone
+ ///
+ /// Whether a property-based clone was performed
+ ///
+ protected static void VerifyClone(
+ HierarchicalReferenceType original, HierarchicalReferenceType clone,
+ bool isDeepClone, bool isPropertyBasedClone
+ ) {
+ if(isPropertyBasedClone) {
+ Assert.AreEqual(0, clone.TestField);
+ Assert.AreEqual(0, clone.ValueTypeField.TestField);
+ Assert.AreEqual(0, clone.ValueTypeField.TestProperty);
+ Assert.AreEqual(0, clone.ValueTypeProperty.TestField);
+ Assert.IsNull(clone.ReferenceTypeField);
+
+ if(isDeepClone) {
+ Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
+ );
+ Assert.AreEqual(0, clone.ReferenceTypeProperty.TestField);
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty[1, 3][0],
+ clone.ReferenceTypeArrayProperty[1, 3][0]
+ );
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty[1, 3][2],
+ clone.ReferenceTypeArrayProperty[1, 3][2]
+ );
+ Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][0].TestField);
+ Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][2].TestField);
+ } else {
+ Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
+ Assert.AreSame(
+ original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
+ );
+ }
+ } else {
+ Assert.AreEqual(original.TestField, clone.TestField);
+ Assert.AreEqual(original.ValueTypeField.TestField, clone.ValueTypeField.TestField);
+ Assert.AreEqual(original.ValueTypeField.TestProperty, clone.ValueTypeField.TestProperty);
+ Assert.AreEqual(
+ original.ValueTypeProperty.TestField, clone.ValueTypeProperty.TestField
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeField.TestField, clone.ReferenceTypeField.TestField
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeField.TestProperty, clone.ReferenceTypeField.TestProperty
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeProperty.TestField, clone.ReferenceTypeProperty.TestField
+ );
+
+ if(isDeepClone) {
+ Assert.AreNotSame(original.ReferenceTypeField, clone.ReferenceTypeField);
+ Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
+ );
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
+ );
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty[1, 3][0],
+ clone.ReferenceTypeArrayProperty[1, 3][0]
+ );
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty[1, 3][2],
+ clone.ReferenceTypeArrayProperty[1, 3][2]
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeArrayProperty[1, 3][0].TestField,
+ clone.ReferenceTypeArrayProperty[1, 3][0].TestField
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeArrayProperty[1, 3][2].TestField,
+ clone.ReferenceTypeArrayProperty[1, 3][2].TestField
+ );
+ } else {
+ Assert.AreSame(original.ReferenceTypeField, clone.ReferenceTypeField);
+ Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
+ Assert.AreSame(
+ original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
+ );
+ Assert.AreSame(
+ original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
+ );
+ }
+ }
+
+ Assert.AreEqual(original.TestProperty, clone.TestProperty);
+ Assert.AreEqual(
+ original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeProperty.TestProperty, clone.ReferenceTypeProperty.TestProperty
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeArrayProperty[1, 3][0].TestProperty,
+ clone.ReferenceTypeArrayProperty[1, 3][0].TestProperty
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeArrayProperty[1, 3][2].TestProperty,
+ clone.ReferenceTypeArrayProperty[1, 3][2].TestProperty
+ );
+ }
+
+ ///
+ /// Verifies that a cloned object exhibits the expected state for the type of
+ /// clone that has been performed
+ ///
+ /// Original instance the clone was created from
+ /// Cloned instance that will be checked for correctness
+ /// Whether the cloned instance is a deep clone
+ ///
+ /// Whether a property-based clone was performed
+ ///
+ protected static void VerifyClone(
+ ref HierarchicalValueType original, ref HierarchicalValueType clone,
+ bool isDeepClone, bool isPropertyBasedClone
+ ) {
+ if(isPropertyBasedClone) {
+ Assert.AreEqual(0, clone.TestField);
+ Assert.AreEqual(0, clone.ValueTypeField.TestField);
+ Assert.AreEqual(0, clone.ValueTypeField.TestProperty);
+ Assert.AreEqual(0, clone.ValueTypeProperty.TestField);
+ Assert.IsNull(clone.ReferenceTypeField);
+
+ if(isDeepClone) {
+ Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
+ );
+ Assert.AreEqual(0, clone.ReferenceTypeProperty.TestField);
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty[1, 3][0],
+ clone.ReferenceTypeArrayProperty[1, 3][0]
+ );
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty[1, 3][2],
+ clone.ReferenceTypeArrayProperty[1, 3][2]
+ );
+ Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][0].TestField);
+ Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][2].TestField);
+ } else {
+ Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
+ Assert.AreSame(
+ original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
+ );
+ }
+ } else {
+ Assert.AreEqual(original.TestField, clone.TestField);
+ Assert.AreEqual(original.ValueTypeField.TestField, clone.ValueTypeField.TestField);
+ Assert.AreEqual(original.ValueTypeField.TestProperty, clone.ValueTypeField.TestProperty);
+ Assert.AreEqual(
+ original.ValueTypeProperty.TestField, clone.ValueTypeProperty.TestField
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeField.TestField, clone.ReferenceTypeField.TestField
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeField.TestProperty, clone.ReferenceTypeField.TestProperty
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeProperty.TestField, clone.ReferenceTypeProperty.TestField
+ );
+
+ if(isDeepClone) {
+ Assert.AreNotSame(original.ReferenceTypeField, clone.ReferenceTypeField);
+ Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
+ );
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
+ );
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty[1, 3][0],
+ clone.ReferenceTypeArrayProperty[1, 3][0]
+ );
+ Assert.AreNotSame(
+ original.ReferenceTypeArrayProperty[1, 3][2],
+ clone.ReferenceTypeArrayProperty[1, 3][2]
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeArrayProperty[1, 3][0].TestField,
+ clone.ReferenceTypeArrayProperty[1, 3][0].TestField
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeArrayProperty[1, 3][2].TestField,
+ clone.ReferenceTypeArrayProperty[1, 3][2].TestField
+ );
+ } else {
+ Assert.AreSame(original.ReferenceTypeField, clone.ReferenceTypeField);
+ Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
+ Assert.AreSame(
+ original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
+ );
+ Assert.AreSame(
+ original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
+ );
+ }
+ }
+
+ Assert.AreEqual(original.TestProperty, clone.TestProperty);
+ Assert.AreEqual(
+ original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeProperty.TestProperty, clone.ReferenceTypeProperty.TestProperty
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeArrayProperty[1, 3][0].TestProperty,
+ clone.ReferenceTypeArrayProperty[1, 3][0].TestProperty
+ );
+ Assert.AreEqual(
+ original.ReferenceTypeArrayProperty[1, 3][2].TestProperty,
+ clone.ReferenceTypeArrayProperty[1, 3][2].TestProperty
+ );
+ }
+
+ /// Creates a value type with random data for testing
+ /// A new value type with random data
+ protected static HierarchicalValueType CreateValueType() {
+ return new HierarchicalValueType() {
+ TestField = 123,
+ TestProperty = 321,
+ ReferenceTypeArrayField = new TestReferenceType[2, 4][] {
+ {
+ null, null, null, null
+ },
+ {
+ null, null, null,
+ new TestReferenceType[3] {
+ new TestReferenceType() { TestField = 101, TestProperty = 202 },
+ null,
+ new TestReferenceType() { TestField = 909, TestProperty = 808 }
+ }
+ },
+ },
+ ReferenceTypeArrayProperty = new TestReferenceType[2, 4][] {
+ {
+ null, null, null, null
+ },
+ {
+ null, null, null,
+ new TestReferenceType[3] {
+ new TestReferenceType() { TestField = 303, TestProperty = 404 },
+ null,
+ new TestReferenceType() { TestField = 707, TestProperty = 606 }
+ }
+ },
+ },
+ ValueTypeField = new TestValueType() {
+ TestField = 456,
+ TestProperty = 654
+ },
+ ValueTypeProperty = new TestValueType() {
+ TestField = 789,
+ TestProperty = 987,
+ },
+ ReferenceTypeField = new TestReferenceType() {
+ TestField = 135,
+ TestProperty = 531
+ },
+ ReferenceTypeProperty = new TestReferenceType() {
+ TestField = 246,
+ TestProperty = 642,
+ }
+ };
+ }
+
+ /// Creates a reference type with random data for testing
+ /// A new reference type with random data
+ protected static HierarchicalReferenceType CreateReferenceType() {
+ return new HierarchicalReferenceType() {
+ TestField = 123,
+ TestProperty = 321,
+ ReferenceTypeArrayField = new TestReferenceType[2, 4][] {
+ {
+ null, null, null, null
+ },
+ {
+ null, null, null,
+ new TestReferenceType[3] {
+ new TestReferenceType() { TestField = 101, TestProperty = 202 },
+ null,
+ new TestReferenceType() { TestField = 909, TestProperty = 808 }
+ }
+ },
+ },
+ ReferenceTypeArrayProperty = new TestReferenceType[2, 4][] {
+ {
+ null, null, null, null
+ },
+ {
+ null, null, null,
+ new TestReferenceType[3] {
+ new TestReferenceType() { TestField = 303, TestProperty = 404 },
+ null,
+ new TestReferenceType() { TestField = 707, TestProperty = 606 }
+ }
+ },
+ },
+ ValueTypeField = new TestValueType() {
+ TestField = 456,
+ TestProperty = 654
+ },
+ ValueTypeProperty = new TestValueType() {
+ TestField = 789,
+ TestProperty = 987,
+ },
+ ReferenceTypeField = new TestReferenceType() {
+ TestField = 135,
+ TestProperty = 531
+ },
+ ReferenceTypeProperty = new TestReferenceType() {
+ TestField = 246,
+ TestProperty = 642,
+ }
+ };
+ }
+
+ }
+
+} // namespace Nuclex.Support.Cloning
+
+#endif // UNITTEST
diff --git a/Source/Cloning/ReflectionCloner.Test.cs b/Source/Cloning/ReflectionCloner.Test.cs
index 683171a..28da7c1 100644
--- a/Source/Cloning/ReflectionCloner.Test.cs
+++ b/Source/Cloning/ReflectionCloner.Test.cs
@@ -29,93 +29,18 @@ namespace Nuclex.Support.Cloning {
/// Unit Test for the reflection-based cloner
[TestFixture]
- public class ReflectionClonerTest {
-
- #region class TestReferenceType
-
- /// A reference type being used for testing
- private class TestReferenceType {
-
- /// Field holding an integer value for testing
- public int TestField;
- /// Property holding an integer value for testing
- public int TestProperty { get; set; }
+ public class ReflectionClonerTest : CloneFactoryTest {
+ /// Initializes a new unit test suite for the reflection cloner
+ public ReflectionClonerTest() {
+ this.cloneFactory = new ReflectionCloner();
}
- #endregion // class TestReferenceType
-
- #region struct TestValueType
-
- /// A value type being used for testing
- private struct TestValueType {
-
- /// Field holding an integer value for testing
- public int TestField;
- /// Property holding an integer value for testing
- public int TestProperty { get; set; }
-
- }
-
- #endregion // struct TestValueType
-
- #region struct HierarchicalValueType
-
- /// A value type containiner other complex types used for testing
- private struct HierarchicalValueType {
-
- /// Field holding an integer value for testing
- public int TestField;
- /// Property holding an integer value for testing
- public int TestProperty { get; set; }
- /// Value type field for testing
- public TestValueType ValueTypeField;
- /// Value type property for testing
- public TestValueType ValueTypeProperty { get; set; }
- /// Reference type field for testing
- public TestReferenceType ReferenceTypeField;
- /// Reference type property for testing
- public TestReferenceType ReferenceTypeProperty { get; set; }
- /// An array field of reference types
- public TestReferenceType[,][] ReferenceTypeArrayField;
- /// An array property of reference types
- public TestReferenceType[,][] ReferenceTypeArrayProperty { get; set; }
-
- }
-
- #endregion // struct HierarchicalValueType
-
- #region struct HierarchicalReferenceType
-
- /// A value type containiner other complex types used for testing
- private class HierarchicalReferenceType {
-
- /// Field holding an integer value for testing
- public int TestField;
- /// Property holding an integer value for testing
- public int TestProperty { get; set; }
- /// Value type field for testing
- public TestValueType ValueTypeField;
- /// Value type property for testing
- public TestValueType ValueTypeProperty { get; set; }
- /// Reference type field for testing
- public TestReferenceType ReferenceTypeField;
- /// Reference type property for testing
- public TestReferenceType ReferenceTypeProperty { get; set; }
- /// An array field of reference types
- public TestReferenceType[,][] ReferenceTypeArrayField;
- /// An array property of reference types
- public TestReferenceType[,][] ReferenceTypeArrayProperty { get; set; }
-
- }
-
- #endregion // struct HierarchicalReferenceType
-
/// Verifies that clones of primitive types can be created
[Test]
public void PrimitiveTypesCanBeCloned() {
int original = 12345;
- int clone = (new ReflectionCloner()).ShallowClone(original, false);
+ int clone = this.cloneFactory.ShallowClone(original, false);
Assert.AreEqual(original, clone);
}
@@ -125,7 +50,7 @@ namespace Nuclex.Support.Cloning {
var original = new TestReferenceType[] {
new TestReferenceType() { TestField = 123, TestProperty = 456 }
};
- TestReferenceType[] clone = (new ReflectionCloner()).ShallowClone(original, false);
+ TestReferenceType[] clone = this.cloneFactory.ShallowClone(original, false);
Assert.AreSame(original[0], clone[0]);
}
@@ -136,7 +61,7 @@ namespace Nuclex.Support.Cloning {
var original = new TestReferenceType[] {
new TestReferenceType() { TestField = 123, TestProperty = 456 }
};
- TestReferenceType[] clone = (new ReflectionCloner()).DeepClone(original, false);
+ TestReferenceType[] clone = this.cloneFactory.DeepClone(original, false);
Assert.AreNotSame(original[0], clone[0]);
Assert.AreEqual(original[0].TestField, clone[0].TestField);
@@ -147,7 +72,7 @@ namespace Nuclex.Support.Cloning {
[Test]
public void GenericListsCanBeCloned() {
var original = new List(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
- List clone = (new ReflectionCloner()).DeepClone(original, false);
+ List clone = this.cloneFactory.DeepClone(original, false);
CollectionAssert.AreEqual(original, clone);
}
@@ -157,7 +82,7 @@ namespace Nuclex.Support.Cloning {
public void GenericDictionariesCanBeCloned() {
var original = new Dictionary();
original.Add(1, "one");
- Dictionary clone = (new ReflectionCloner()).DeepClone(original, false);
+ Dictionary clone = this.cloneFactory.DeepClone(original, false);
Assert.AreEqual("one", clone[1]);
}
@@ -167,9 +92,9 @@ namespace Nuclex.Support.Cloning {
///
[Test]
public void ShallowFieldBasedClonesOfValueTypesCanBeMade() {
- HierarchicalValueType original = createValueType();
- HierarchicalValueType clone = (new ReflectionCloner()).ShallowClone(original, false);
- verifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: false);
+ HierarchicalValueType original = CreateValueType();
+ HierarchicalValueType clone = this.cloneFactory.ShallowClone(original, false);
+ VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: false);
}
///
@@ -177,9 +102,9 @@ namespace Nuclex.Support.Cloning {
///
[Test]
public void ShallowFieldBasedClonesOfReferenceTypesCanBeMade() {
- HierarchicalReferenceType original = createReferenceType();
- HierarchicalReferenceType clone = (new ReflectionCloner()).ShallowClone(original, false);
- verifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: false);
+ HierarchicalReferenceType original = CreateReferenceType();
+ HierarchicalReferenceType clone = this.cloneFactory.ShallowClone(original, false);
+ VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: false);
}
///
@@ -187,9 +112,9 @@ namespace Nuclex.Support.Cloning {
///
[Test]
public void DeepFieldBasedClonesOfValueTypesCanBeMade() {
- HierarchicalValueType original = createValueType();
- HierarchicalValueType clone = (new ReflectionCloner()).DeepClone(original, false);
- verifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
+ HierarchicalValueType original = CreateValueType();
+ HierarchicalValueType clone = this.cloneFactory.DeepClone(original, false);
+ VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
}
///
@@ -197,9 +122,9 @@ namespace Nuclex.Support.Cloning {
///
[Test]
public void DeepFieldBasedClonesOfReferenceTypesCanBeMade() {
- HierarchicalReferenceType original = createReferenceType();
- HierarchicalReferenceType clone = (new ReflectionCloner()).DeepClone(original, false);
- verifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
+ HierarchicalReferenceType original = CreateReferenceType();
+ HierarchicalReferenceType clone = this.cloneFactory.DeepClone(original, false);
+ VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
}
///
@@ -207,9 +132,9 @@ namespace Nuclex.Support.Cloning {
///
[Test]
public void ShallowPropertyBasedClonesOfValueTypesCanBeMade() {
- HierarchicalValueType original = createValueType();
- HierarchicalValueType clone = (new ReflectionCloner()).ShallowClone(original, true);
- verifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: true);
+ HierarchicalValueType original = CreateValueType();
+ HierarchicalValueType clone = this.cloneFactory.ShallowClone(original, true);
+ VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: true);
}
///
@@ -217,9 +142,9 @@ namespace Nuclex.Support.Cloning {
///
[Test]
public void ShallowPropertyBasedClonesOfReferenceTypesCanBeMade() {
- HierarchicalReferenceType original = createReferenceType();
- HierarchicalReferenceType clone = (new ReflectionCloner()).ShallowClone(original, true);
- verifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: true);
+ HierarchicalReferenceType original = CreateReferenceType();
+ HierarchicalReferenceType clone = this.cloneFactory.ShallowClone(original, true);
+ VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: true);
}
///
@@ -227,9 +152,9 @@ namespace Nuclex.Support.Cloning {
///
[Test]
public void DeepPropertyBasedClonesOfValueTypesCanBeMade() {
- HierarchicalValueType original = createValueType();
- HierarchicalValueType clone = (new ReflectionCloner()).DeepClone(original, true);
- verifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
+ HierarchicalValueType original = CreateValueType();
+ HierarchicalValueType clone = this.cloneFactory.DeepClone(original, true);
+ VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
}
///
@@ -237,340 +162,13 @@ namespace Nuclex.Support.Cloning {
///
[Test]
public void DeepPropertyBasedClonesOfReferenceTypesCanBeMade() {
- HierarchicalReferenceType original = createReferenceType();
- HierarchicalReferenceType clone = (new ReflectionCloner()).DeepClone(original, true);
- verifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
+ HierarchicalReferenceType original = CreateReferenceType();
+ HierarchicalReferenceType clone = this.cloneFactory.DeepClone(original, true);
+ VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
}
- ///
- /// Verifies that a cloned object exhibits the expected state for the type of
- /// clone that has been performed
- ///
- /// Original instance the clone was created from
- /// Cloned instance that will be checked for correctness
- /// Whether the cloned instance is a deep clone
- ///
- /// Whether a property-based clone was performed
- ///
- private static void verifyClone(
- HierarchicalReferenceType original, HierarchicalReferenceType clone,
- bool isDeepClone, bool isPropertyBasedClone
- ) {
- if(isPropertyBasedClone) {
- Assert.AreEqual(0, clone.TestField);
- Assert.AreEqual(0, clone.ValueTypeField.TestField);
- Assert.AreEqual(0, clone.ValueTypeField.TestProperty);
- Assert.AreEqual(0, clone.ValueTypeProperty.TestField);
- Assert.IsNull(clone.ReferenceTypeField);
-
- if(isDeepClone) {
- Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
- );
- Assert.AreEqual(0, clone.ReferenceTypeProperty.TestField);
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty[1, 3][0],
- clone.ReferenceTypeArrayProperty[1, 3][0]
- );
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty[1, 3][2],
- clone.ReferenceTypeArrayProperty[1, 3][2]
- );
- Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][0].TestField);
- Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][2].TestField);
- } else {
- Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
- Assert.AreSame(
- original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
- );
- }
- } else {
- Assert.AreEqual(original.TestField, clone.TestField);
- Assert.AreEqual(original.ValueTypeField.TestField, clone.ValueTypeField.TestField);
- Assert.AreEqual(original.ValueTypeField.TestProperty, clone.ValueTypeField.TestProperty);
- Assert.AreEqual(
- original.ValueTypeProperty.TestField, clone.ValueTypeProperty.TestField
- );
- Assert.AreEqual(
- original.ReferenceTypeField.TestField, clone.ReferenceTypeField.TestField
- );
- Assert.AreEqual(
- original.ReferenceTypeField.TestProperty, clone.ReferenceTypeField.TestProperty
- );
- Assert.AreEqual(
- original.ReferenceTypeProperty.TestField, clone.ReferenceTypeProperty.TestField
- );
-
- if(isDeepClone) {
- Assert.AreNotSame(original.ReferenceTypeField, clone.ReferenceTypeField);
- Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
- Assert.AreNotSame(
- original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
- );
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
- );
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty[1, 3][0],
- clone.ReferenceTypeArrayProperty[1, 3][0]
- );
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty[1, 3][2],
- clone.ReferenceTypeArrayProperty[1, 3][2]
- );
- Assert.AreEqual(
- original.ReferenceTypeArrayProperty[1, 3][0].TestField,
- clone.ReferenceTypeArrayProperty[1, 3][0].TestField
- );
- Assert.AreEqual(
- original.ReferenceTypeArrayProperty[1, 3][2].TestField,
- clone.ReferenceTypeArrayProperty[1, 3][2].TestField
- );
- } else {
- Assert.AreSame(original.ReferenceTypeField, clone.ReferenceTypeField);
- Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
- Assert.AreSame(
- original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
- );
- Assert.AreSame(
- original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
- );
- }
- }
-
- Assert.AreEqual(original.TestProperty, clone.TestProperty);
- Assert.AreEqual(
- original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
- );
- Assert.AreEqual(
- original.ReferenceTypeProperty.TestProperty, clone.ReferenceTypeProperty.TestProperty
- );
- Assert.AreEqual(
- original.ReferenceTypeArrayProperty[1, 3][0].TestProperty,
- clone.ReferenceTypeArrayProperty[1, 3][0].TestProperty
- );
- Assert.AreEqual(
- original.ReferenceTypeArrayProperty[1, 3][2].TestProperty,
- clone.ReferenceTypeArrayProperty[1, 3][2].TestProperty
- );
- }
-
- ///
- /// Verifies that a cloned object exhibits the expected state for the type of
- /// clone that has been performed
- ///
- /// Original instance the clone was created from
- /// Cloned instance that will be checked for correctness
- /// Whether the cloned instance is a deep clone
- ///
- /// Whether a property-based clone was performed
- ///
- private static void verifyClone(
- ref HierarchicalValueType original, ref HierarchicalValueType clone,
- bool isDeepClone, bool isPropertyBasedClone
- ) {
- if(isPropertyBasedClone) {
- Assert.AreEqual(0, clone.TestField);
- Assert.AreEqual(0, clone.ValueTypeField.TestField);
- Assert.AreEqual(0, clone.ValueTypeField.TestProperty);
- Assert.AreEqual(0, clone.ValueTypeProperty.TestField);
- Assert.IsNull(clone.ReferenceTypeField);
-
- if(isDeepClone) {
- Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
- );
- Assert.AreEqual(0, clone.ReferenceTypeProperty.TestField);
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty[1, 3][0],
- clone.ReferenceTypeArrayProperty[1, 3][0]
- );
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty[1, 3][2],
- clone.ReferenceTypeArrayProperty[1, 3][2]
- );
- Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][0].TestField);
- Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][2].TestField);
- } else {
- Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
- Assert.AreSame(
- original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
- );
- }
- } else {
- Assert.AreEqual(original.TestField, clone.TestField);
- Assert.AreEqual(original.ValueTypeField.TestField, clone.ValueTypeField.TestField);
- Assert.AreEqual(original.ValueTypeField.TestProperty, clone.ValueTypeField.TestProperty);
- Assert.AreEqual(
- original.ValueTypeProperty.TestField, clone.ValueTypeProperty.TestField
- );
- Assert.AreEqual(
- original.ReferenceTypeField.TestField, clone.ReferenceTypeField.TestField
- );
- Assert.AreEqual(
- original.ReferenceTypeField.TestProperty, clone.ReferenceTypeField.TestProperty
- );
- Assert.AreEqual(
- original.ReferenceTypeProperty.TestField, clone.ReferenceTypeProperty.TestField
- );
-
- if(isDeepClone) {
- Assert.AreNotSame(original.ReferenceTypeField, clone.ReferenceTypeField);
- Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
- Assert.AreNotSame(
- original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
- );
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
- );
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty[1, 3][0],
- clone.ReferenceTypeArrayProperty[1, 3][0]
- );
- Assert.AreNotSame(
- original.ReferenceTypeArrayProperty[1, 3][2],
- clone.ReferenceTypeArrayProperty[1, 3][2]
- );
- Assert.AreEqual(
- original.ReferenceTypeArrayProperty[1, 3][0].TestField,
- clone.ReferenceTypeArrayProperty[1, 3][0].TestField
- );
- Assert.AreEqual(
- original.ReferenceTypeArrayProperty[1, 3][2].TestField,
- clone.ReferenceTypeArrayProperty[1, 3][2].TestField
- );
- } else {
- Assert.AreSame(original.ReferenceTypeField, clone.ReferenceTypeField);
- Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
- Assert.AreSame(
- original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
- );
- Assert.AreSame(
- original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
- );
- }
- }
-
- Assert.AreEqual(original.TestProperty, clone.TestProperty);
- Assert.AreEqual(
- original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
- );
- Assert.AreEqual(
- original.ReferenceTypeProperty.TestProperty, clone.ReferenceTypeProperty.TestProperty
- );
- Assert.AreEqual(
- original.ReferenceTypeArrayProperty[1, 3][0].TestProperty,
- clone.ReferenceTypeArrayProperty[1, 3][0].TestProperty
- );
- Assert.AreEqual(
- original.ReferenceTypeArrayProperty[1, 3][2].TestProperty,
- clone.ReferenceTypeArrayProperty[1, 3][2].TestProperty
- );
- }
-
- /// Creates a value type with random data for testing
- /// A new value type with random data
- private static HierarchicalValueType createValueType() {
- return new HierarchicalValueType() {
- TestField = 123,
- TestProperty = 321,
- ReferenceTypeArrayField = new TestReferenceType[2, 4][] {
- {
- null, null, null, null
- },
- {
- null, null, null,
- new TestReferenceType[3] {
- new TestReferenceType() { TestField = 101, TestProperty = 202 },
- null,
- new TestReferenceType() { TestField = 909, TestProperty = 808 }
- }
- },
- },
- ReferenceTypeArrayProperty = new TestReferenceType[2, 4][] {
- {
- null, null, null, null
- },
- {
- null, null, null,
- new TestReferenceType[3] {
- new TestReferenceType() { TestField = 303, TestProperty = 404 },
- null,
- new TestReferenceType() { TestField = 707, TestProperty = 606 }
- }
- },
- },
- ValueTypeField = new TestValueType() {
- TestField = 456,
- TestProperty = 654
- },
- ValueTypeProperty = new TestValueType() {
- TestField = 789,
- TestProperty = 987,
- },
- ReferenceTypeField = new TestReferenceType() {
- TestField = 135,
- TestProperty = 531
- },
- ReferenceTypeProperty = new TestReferenceType() {
- TestField = 246,
- TestProperty = 642,
- }
- };
- }
-
- /// Creates a reference type with random data for testing
- /// A new reference type with random data
- private static HierarchicalReferenceType createReferenceType() {
- return new HierarchicalReferenceType() {
- TestField = 123,
- TestProperty = 321,
- ReferenceTypeArrayField = new TestReferenceType[2, 4][] {
- {
- null, null, null, null
- },
- {
- null, null, null,
- new TestReferenceType[3] {
- new TestReferenceType() { TestField = 101, TestProperty = 202 },
- null,
- new TestReferenceType() { TestField = 909, TestProperty = 808 }
- }
- },
- },
- ReferenceTypeArrayProperty = new TestReferenceType[2, 4][] {
- {
- null, null, null, null
- },
- {
- null, null, null,
- new TestReferenceType[3] {
- new TestReferenceType() { TestField = 303, TestProperty = 404 },
- null,
- new TestReferenceType() { TestField = 707, TestProperty = 606 }
- }
- },
- },
- ValueTypeField = new TestValueType() {
- TestField = 456,
- TestProperty = 654
- },
- ValueTypeProperty = new TestValueType() {
- TestField = 789,
- TestProperty = 987,
- },
- ReferenceTypeField = new TestReferenceType() {
- TestField = 135,
- TestProperty = 531
- },
- ReferenceTypeProperty = new TestReferenceType() {
- TestField = 246,
- TestProperty = 642,
- }
- };
- }
+ /// Clone factory being tested
+ private ICloneFactory cloneFactory;
}
diff --git a/Source/Cloning/ReflectionCloner.cs b/Source/Cloning/ReflectionCloner.cs
index 357d49c..a145255 100644
--- a/Source/Cloning/ReflectionCloner.cs
+++ b/Source/Cloning/ReflectionCloner.cs
@@ -24,6 +24,13 @@ using System.Reflection;
namespace Nuclex.Support.Cloning {
/// Clones objects using reflection
+ ///
+ ///
+ /// This type of cloning is a lot faster than cloning by serialization and
+ /// incurs no set-up cost, but requires cloned types to provide a default
+ /// constructor in order to work.
+ ///
+ ///
public class ReflectionCloner : ICloneFactory {
///
diff --git a/Source/Cloning/SerializationCloner.Test.cs b/Source/Cloning/SerializationCloner.Test.cs
new file mode 100644
index 0000000..75cbe11
--- /dev/null
+++ b/Source/Cloning/SerializationCloner.Test.cs
@@ -0,0 +1,126 @@
+#region CPL License
+/*
+Nuclex Framework
+Copyright (C) 2002-2010 Nuclex Development Labs
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the IBM Common Public License as
+published by the IBM Corporation; either version 1.0 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+IBM Common Public License for more details.
+
+You should have received a copy of the IBM Common Public
+License along with this library
+*/
+#endregion
+
+#if UNITTEST
+
+using System;
+using System.Collections.Generic;
+
+using NUnit.Framework;
+
+namespace Nuclex.Support.Cloning {
+
+ /// Unit Test for the reflection-based cloner
+ [TestFixture]
+ public class SerializationClonerTest : CloneFactoryTest {
+
+ /// Initializes a new unit test suite for the reflection cloner
+ public SerializationClonerTest() {
+ this.cloneFactory = new SerializationCloner();
+ }
+
+ /// Verifies that clones of primitive types can be created
+ [Test]
+ public void PrimitiveTypesCanBeCloned() {
+ int original = 12345;
+ int clone = this.cloneFactory.DeepClone(original, false);
+ Assert.AreEqual(original, clone);
+ }
+
+ /// Verifies that deep clones of arrays can be made
+ [Test]
+ public void DeepClonesOfArraysCanBeMade() {
+ var original = new TestReferenceType[] {
+ new TestReferenceType() { TestField = 123, TestProperty = 456 }
+ };
+ TestReferenceType[] clone = this.cloneFactory.DeepClone(original, false);
+
+ Assert.AreNotSame(original[0], clone[0]);
+ Assert.AreEqual(original[0].TestField, clone[0].TestField);
+ Assert.AreEqual(original[0].TestProperty, clone[0].TestProperty);
+ }
+
+ /// Verifies that deep clones of a generic list can be made
+ [Test]
+ public void GenericListsCanBeCloned() {
+ var original = new List(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+ List clone = this.cloneFactory.DeepClone(original, false);
+
+ CollectionAssert.AreEqual(original, clone);
+ }
+
+ /// Verifies that deep clones of a generic dictionary can be made
+ [Test]
+ public void GenericDictionariesCanBeCloned() {
+ var original = new Dictionary();
+ original.Add(1, "one");
+ Dictionary clone = this.cloneFactory.DeepClone(original, false);
+
+ Assert.AreEqual("one", clone[1]);
+ }
+
+ ///
+ /// Verifies that a field-based deep clone of a value type can be performed
+ ///
+ [Test]
+ public void DeepFieldBasedClonesOfValueTypesCanBeMade() {
+ HierarchicalValueType original = CreateValueType();
+ HierarchicalValueType clone = this.cloneFactory.DeepClone(original, false);
+ VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
+ }
+
+ ///
+ /// Verifies that a field-based deep clone of a reference type can be performed
+ ///
+ [Test]
+ public void DeepFieldBasedClonesOfReferenceTypesCanBeMade() {
+ HierarchicalReferenceType original = CreateReferenceType();
+ HierarchicalReferenceType clone = this.cloneFactory.DeepClone(original, false);
+ VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
+ }
+
+ ///
+ /// Verifies that a property-based deep clone of a value type can be performed
+ ///
+ [Test]
+ public void DeepPropertyBasedClonesOfValueTypesCanBeMade() {
+ HierarchicalValueType original = CreateValueType();
+ HierarchicalValueType clone = this.cloneFactory.DeepClone(original, true);
+ VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
+ }
+
+ ///
+ /// Verifies that a property-based deep clone of a reference type can be performed
+ ///
+ [Test]
+ public void DeepPropertyBasedClonesOfReferenceTypesCanBeMade() {
+ HierarchicalReferenceType original = CreateReferenceType();
+ HierarchicalReferenceType clone = this.cloneFactory.DeepClone(original, true);
+ VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
+ }
+
+ /// Clone factory being tested
+ private ICloneFactory cloneFactory;
+
+ }
+
+} // namespace Nuclex.Support.Cloning
+
+#endif // UNITTEST
diff --git a/Source/Cloning/SerializationCloner.cs b/Source/Cloning/SerializationCloner.cs
new file mode 100644
index 0000000..8cf973a
--- /dev/null
+++ b/Source/Cloning/SerializationCloner.cs
@@ -0,0 +1,288 @@
+#region CPL License
+/*
+Nuclex Framework
+Copyright (C) 2002-2010 Nuclex Development Labs
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the IBM Common Public License as
+published by the IBM Corporation; either version 1.0 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+IBM Common Public License for more details.
+
+You should have received a copy of the IBM Common Public
+License along with this library
+*/
+#endregion
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+
+namespace Nuclex.Support.Cloning {
+
+ /// Clones objects via serialization
+ ///
+ ///
+ /// This type of cloning uses the binary formatter to persist the state of
+ /// an object and then restores it into a clone. It has the advantage of even
+ /// working with types that don't provide a default constructor, but is
+ /// terribly slow.
+ ///
+ ///
+ /// Inspired by the "A Generic Method for Deep Cloning in C# 3.0" article
+ /// on CodeProject: http://www.codeproject.com/KB/cs/generic_deep_cloning.aspx
+ ///
+ ///
+ public class SerializationCloner : ICloneFactory {
+
+ #region class StaticSurrogateSelector
+
+ /// Selects a static surrogate for any non-primitive types
+ private class StaticSurrogateSelector : ISurrogateSelector {
+
+ /// Initializes a new static surrogate selector
+ /// Surrogate that will be selected
+ public StaticSurrogateSelector(ISerializationSurrogate staticSurrogate) {
+ this.staticSurrogate = staticSurrogate;
+ }
+
+ ///
+ /// Sets the next selector to escalate to if this one can't provide a surrogate
+ ///
+ /// Selector to escalate to
+ public void ChainSelector(ISurrogateSelector selector) {
+ this.chainedSelector = selector;
+ }
+
+ ///
+ /// Returns the selector this one will escalate to if it can't provide a surrogate
+ ///
+ /// The selector this one will escalate to
+ public ISurrogateSelector GetNextSelector() {
+ return this.chainedSelector;
+ }
+
+ /// Attempts to provides a surrogate for the specified type
+ /// Type a surrogate will be provided for
+ /// Context
+ ///
+ ///
+ public ISerializationSurrogate GetSurrogate(
+ Type type, StreamingContext context, out ISurrogateSelector selector
+ ) {
+ if(type.IsPrimitive || type.IsArray || (type == typeof(string))) {
+ if(this.chainedSelector == null) {
+ selector = null;
+ return null;
+ } else {
+ return this.chainedSelector.GetSurrogate(type, context, out selector);
+ }
+ } else {
+ selector = this;
+ return this.staticSurrogate;
+ }
+ }
+
+ /// Surrogate the that will be selected for any non-primitive types
+ private ISerializationSurrogate staticSurrogate;
+ /// Surrogate selector to escalate to if no surrogate can be provided
+ private ISurrogateSelector chainedSelector;
+
+ }
+
+ #endregion // class StaticSurrogateSelector
+
+ #region class FieldSerializationSurrogate
+
+ /// Serializes a type based on its fields
+ private class FieldSerializationSurrogate : ISerializationSurrogate {
+
+ /// Extracts the data to be serialized from an object
+ /// Object that is being serialized
+ /// Stores the serialized informations
+ ///
+ /// Provides additional informations about the serialization process
+ ///
+ public void GetObjectData(
+ object objectToSerialize,
+ SerializationInfo info,
+ StreamingContext context
+ ) {
+ Type originalType = objectToSerialize.GetType();
+
+ FieldInfo[] fieldInfos = originalType.GetFields(
+ BindingFlags.Public | BindingFlags.NonPublic |
+ BindingFlags.Instance | BindingFlags.FlattenHierarchy
+ );
+ for(int index = 0; index < fieldInfos.Length; ++index) {
+ FieldInfo fieldInfo = fieldInfos[index];
+ info.AddValue(fieldInfo.Name, fieldInfo.GetValue(objectToSerialize));
+ }
+ }
+
+ /// Reinserts saved data into a deserializd object
+ /// Object the saved data will be inserted into
+ /// Contains the serialized informations
+ ///
+ /// Provides additional informations about the serialization process
+ ///
+ /// Surrogate selector that specified this surrogate
+ /// The deserialized object
+ public object SetObjectData(
+ object deserializedObject,
+ SerializationInfo info,
+ StreamingContext context,
+ ISurrogateSelector selector
+ ) {
+ Type originalType = deserializedObject.GetType();
+
+ FieldInfo[] fieldInfos = originalType.GetFields(
+ BindingFlags.Public | BindingFlags.NonPublic |
+ BindingFlags.Instance | BindingFlags.FlattenHierarchy
+ );
+ for(int index = 0; index < fieldInfos.Length; ++index) {
+ FieldInfo fieldInfo = fieldInfos[index];
+ fieldInfo.SetValue(deserializedObject, info.GetValue(fieldInfo.Name, fieldInfo.FieldType));
+ }
+
+ return deserializedObject;
+ }
+
+ }
+
+ #endregion // class FieldSerializationSurrogate
+
+ #region class PropertySerializationSurrogate
+
+ /// Serializes a type based on its properties
+ private class PropertySerializationSurrogate : ISerializationSurrogate {
+
+ /// Extracts the data to be serialized from an object
+ /// Object that is being serialized
+ /// Stores the serialized informations
+ ///
+ /// Provides additional informations about the serialization process
+ ///
+ public void GetObjectData(
+ object objectToSerialize,
+ SerializationInfo info,
+ StreamingContext context
+ ) {
+ Type originalType = objectToSerialize.GetType();
+
+ PropertyInfo[] propertyInfos = originalType.GetProperties(
+ BindingFlags.Public | BindingFlags.NonPublic |
+ BindingFlags.Instance | BindingFlags.FlattenHierarchy
+ );
+ for(int index = 0; index < propertyInfos.Length; ++index) {
+ PropertyInfo propertyInfo = propertyInfos[index];
+ info.AddValue(propertyInfo.Name, propertyInfo.GetValue(objectToSerialize, null));
+ }
+ }
+
+ /// Reinserts saved data into a deserializd object
+ /// Object the saved data will be inserted into
+ /// Contains the serialized informations
+ ///
+ /// Provides additional informations about the serialization process
+ ///
+ /// Surrogate selector that specified this surrogate
+ /// The deserialized object
+ public object SetObjectData(
+ object deserializedObject,
+ SerializationInfo info,
+ StreamingContext context,
+ ISurrogateSelector selector
+ ) {
+ Type originalType = deserializedObject.GetType();
+
+ PropertyInfo[] propertyInfos = originalType.GetProperties(
+ BindingFlags.Public | BindingFlags.NonPublic |
+ BindingFlags.Instance | BindingFlags.FlattenHierarchy
+ );
+ for(int index = 0; index < propertyInfos.Length; ++index) {
+ PropertyInfo propertyInfo = propertyInfos[index];
+ propertyInfo.SetValue(
+ deserializedObject,
+ info.GetValue(propertyInfo.Name, propertyInfo.PropertyType),
+ null
+ );
+ }
+
+ return deserializedObject;
+ }
+
+ }
+
+ #endregion // class PropertySerializationSurrogate
+
+ /// Initializes a new serialization-based cloner
+ public SerializationCloner() {
+ var fieldSurrogateSelector = new SurrogateSelector();
+ fieldSurrogateSelector.ChainSelector(
+ new StaticSurrogateSelector(new FieldSerializationSurrogate())
+ );
+ this.fieldBasedFormatter = new BinaryFormatter(
+ fieldSurrogateSelector, new StreamingContext(StreamingContextStates.All)
+ );
+
+ var propertySurrogateSelector = new SurrogateSelector();
+ propertySurrogateSelector.ChainSelector(
+ new StaticSurrogateSelector(new PropertySerializationSurrogate())
+ );
+ this.propertyBasedFormatter = new BinaryFormatter(
+ propertySurrogateSelector, new StreamingContext(StreamingContextStates.All)
+ );
+ }
+
+ ///
+ /// Creates a deep clone of the specified object, also creating clones of all
+ /// child objects being referenced
+ ///
+ /// Type of the object that will be cloned
+ /// Object that will be cloned
+ ///
+ /// Whether to clone the object based on its properties only
+ ///
+ /// A deep clone of the provided object
+ public TCloned DeepClone(TCloned objectToClone, bool usePropertyBasedClone) {
+ using(var memoryStream = new MemoryStream()) {
+ if(usePropertyBasedClone) {
+ this.propertyBasedFormatter.Serialize(memoryStream, objectToClone);
+ memoryStream.Position = 0;
+ return (TCloned)this.propertyBasedFormatter.Deserialize(memoryStream);
+ } else {
+ this.fieldBasedFormatter.Serialize(memoryStream, objectToClone);
+ memoryStream.Position = 0;
+ return (TCloned)this.fieldBasedFormatter.Deserialize(memoryStream);
+ }
+ }
+ }
+
+ ///
+ /// Creates a shallow clone of the specified object, reusing any referenced objects
+ ///
+ /// Type of the object that will be cloned
+ /// Object that will be cloned
+ ///
+ /// Whether to clone the object based on its properties only
+ ///
+ /// A shallow clone of the provided object
+ public TCloned ShallowClone(TCloned objectToClone, bool usePropertyBasedClone) {
+ throw new NotSupportedException("The serialization cloner cannot create shallow clones");
+ }
+
+ /// Serializes objects by storing their fields
+ private BinaryFormatter fieldBasedFormatter;
+ /// Serializes objects by storing their properties
+ private BinaryFormatter propertyBasedFormatter;
+
+ }
+
+} // namespace Nuclex.Support.Cloning