Changed license to Apache License 2.0
This commit is contained in:
parent
d3bf0be9d7
commit
9f36d71529
144 changed files with 32422 additions and 32544 deletions
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,226 +1,225 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 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 !NO_SETS
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Unit Test for the expression tree-based cloner</summary>
|
||||
[TestFixture]
|
||||
internal class ExpressionTreeClonerTest : CloneFactoryTest {
|
||||
|
||||
/// <summary>Initializes a new unit test suite for the reflection cloner</summary>
|
||||
public ExpressionTreeClonerTest() {
|
||||
this.cloneFactory = new ExpressionTreeCloner();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that cloning a null object simply returns null</summary>
|
||||
[Test]
|
||||
public void CloningNullYieldsNull() {
|
||||
Assert.IsNull(this.cloneFactory.DeepFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.DeepPropertyClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowPropertyClone<object>(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that clones of objects whose class doesn't possess a default constructor
|
||||
/// can be made
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ClassWithoutDefaultConstructorCanBeCloned() {
|
||||
var original = new ClassWithoutDefaultConstructor(1234);
|
||||
ClassWithoutDefaultConstructor clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.Dummy, clone.Dummy);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clones of primitive types can be created</summary>
|
||||
[Test]
|
||||
public void PrimitiveTypesCanBeCloned() {
|
||||
int original = 12345;
|
||||
int clone = this.cloneFactory.DeepFieldClone(original);
|
||||
Assert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void ReferenceTypesCanBeCloned() {
|
||||
var original = new TestReferenceType() { TestField = 123, TestProperty = 456 };
|
||||
TestReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.TestField, clone.TestField);
|
||||
Assert.AreEqual(original.TestProperty, clone.TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void PrimitiveArraysCanBeCloned() {
|
||||
var original = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
int[] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void ShallowClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
|
||||
Assert.AreSame(original[0], clone[0]);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void DeepClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[,] {
|
||||
{
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
}
|
||||
};
|
||||
TestReferenceType[,] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original[0, 0], clone[0, 0]);
|
||||
Assert.AreEqual(original[0, 0].TestField, clone[0, 0].TestField);
|
||||
Assert.AreEqual(original[0, 0].TestProperty, clone[0, 0].TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic list can be made</summary>
|
||||
[Test]
|
||||
public void GenericListsCanBeCloned() {
|
||||
var original = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
List<int> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic dictionary can be made</summary>
|
||||
[Test]
|
||||
public void GenericDictionariesCanBeCloned() {
|
||||
var original = new Dictionary<int, string>();
|
||||
original.Add(1, "one");
|
||||
Dictionary<int, string> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreEqual("one", clone[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>Clone factory being tested</summary>
|
||||
private ICloneFactory cloneFactory;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_SETS
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_SETS
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Unit Test for the expression tree-based cloner</summary>
|
||||
[TestFixture]
|
||||
internal class ExpressionTreeClonerTest : CloneFactoryTest {
|
||||
|
||||
/// <summary>Initializes a new unit test suite for the reflection cloner</summary>
|
||||
public ExpressionTreeClonerTest() {
|
||||
this.cloneFactory = new ExpressionTreeCloner();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that cloning a null object simply returns null</summary>
|
||||
[Test]
|
||||
public void CloningNullYieldsNull() {
|
||||
Assert.IsNull(this.cloneFactory.DeepFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.DeepPropertyClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowPropertyClone<object>(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that clones of objects whose class doesn't possess a default constructor
|
||||
/// can be made
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ClassWithoutDefaultConstructorCanBeCloned() {
|
||||
var original = new ClassWithoutDefaultConstructor(1234);
|
||||
ClassWithoutDefaultConstructor clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.Dummy, clone.Dummy);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clones of primitive types can be created</summary>
|
||||
[Test]
|
||||
public void PrimitiveTypesCanBeCloned() {
|
||||
int original = 12345;
|
||||
int clone = this.cloneFactory.DeepFieldClone(original);
|
||||
Assert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void ReferenceTypesCanBeCloned() {
|
||||
var original = new TestReferenceType() { TestField = 123, TestProperty = 456 };
|
||||
TestReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.TestField, clone.TestField);
|
||||
Assert.AreEqual(original.TestProperty, clone.TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void PrimitiveArraysCanBeCloned() {
|
||||
var original = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
int[] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void ShallowClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
|
||||
Assert.AreSame(original[0], clone[0]);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void DeepClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[,] {
|
||||
{
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
}
|
||||
};
|
||||
TestReferenceType[,] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original[0, 0], clone[0, 0]);
|
||||
Assert.AreEqual(original[0, 0].TestField, clone[0, 0].TestField);
|
||||
Assert.AreEqual(original[0, 0].TestProperty, clone[0, 0].TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic list can be made</summary>
|
||||
[Test]
|
||||
public void GenericListsCanBeCloned() {
|
||||
var original = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
List<int> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic dictionary can be made</summary>
|
||||
[Test]
|
||||
public void GenericDictionariesCanBeCloned() {
|
||||
var original = new Dictionary<int, string>();
|
||||
original.Add(1, "one");
|
||||
Dictionary<int, string> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreEqual("one", clone[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>Clone factory being tested</summary>
|
||||
private ICloneFactory cloneFactory;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_SETS
|
||||
|
|
|
@ -1,286 +1,285 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 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 !NO_SETS
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>
|
||||
/// Cloning factory which uses expression trees to improve performance when cloning
|
||||
/// is a high-frequency action.
|
||||
/// </summary>
|
||||
public partial class ExpressionTreeCloner : ICloneFactory {
|
||||
|
||||
/// <summary>Initializes the static members of the expression tree cloner</summary>
|
||||
static ExpressionTreeCloner() {
|
||||
shallowFieldBasedCloners = new ConcurrentDictionary<Type, Func<object, object>>();
|
||||
deepFieldBasedCloners = new ConcurrentDictionary<Type, Func<object, object>>();
|
||||
shallowPropertyBasedCloners = new ConcurrentDictionary<Type, Func<object, object>>();
|
||||
deepPropertyBasedCloners = new ConcurrentDictionary<Type, Func<object, object>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToCloneAsObject == null) {
|
||||
return default(TCloned);
|
||||
}
|
||||
|
||||
Func<object, object> cloner = getOrCreateDeepFieldBasedCloner(typeof(TCloned));
|
||||
return (TCloned)cloner(objectToCloneAsObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToCloneAsObject == null) {
|
||||
return default(TCloned);
|
||||
}
|
||||
|
||||
Func<object, object> cloner = getOrCreateDeepPropertyBasedCloner(typeof(TCloned));
|
||||
return (TCloned)cloner(objectToCloneAsObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
public static TCloned ShallowFieldClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToCloneAsObject == null) {
|
||||
return default(TCloned);
|
||||
}
|
||||
|
||||
Func<object, object> cloner = getOrCreateShallowFieldBasedCloner(typeof(TCloned));
|
||||
return (TCloned)cloner(objectToCloneAsObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
public static TCloned ShallowPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToCloneAsObject == null) {
|
||||
return default(TCloned);
|
||||
}
|
||||
|
||||
Func<object, object> cloner = getOrCreateShallowPropertyBasedCloner(typeof(TCloned));
|
||||
return (TCloned)cloner(objectToCloneAsObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowFieldClone<TCloned>(TCloned objectToClone) {
|
||||
return ExpressionTreeCloner.ShallowFieldClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
return ExpressionTreeCloner.ShallowPropertyClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
return ExpressionTreeCloner.DeepFieldClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
return ExpressionTreeCloner.DeepPropertyClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// Transfers the state of one object into another, creating clones of referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
public void DeepCopyState<TState>(TState original, TState target, bool propertyBased)
|
||||
where TState : class {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transfers the state of one object into another, creating clones of referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
public void DeepCopyState<TState>(ref TState original, ref TState target, bool propertyBased)
|
||||
where TState : struct {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>Transfers the state of one object into another</summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
public void ShallowCopyState<TState>(TState original, TState target, bool propertyBased)
|
||||
where TState : class {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>Transfers the state of one object into another</summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
public void ShallowCopyState<TState>(ref TState original, ref TState target, bool propertyBased)
|
||||
where TState : struct {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the existing clone method for the specified type or compiles one if
|
||||
/// none exists for the type yet
|
||||
/// </summary>
|
||||
/// <param name="clonedType">Type for which a clone method will be retrieved</param>
|
||||
/// <returns>The clone method for the specified type</returns>
|
||||
private static Func<object, object> getOrCreateShallowFieldBasedCloner(Type clonedType) {
|
||||
Func<object, object> cloner;
|
||||
|
||||
if(!shallowFieldBasedCloners.TryGetValue(clonedType, out cloner)) {
|
||||
cloner = createShallowFieldBasedCloner(clonedType);
|
||||
shallowFieldBasedCloners.TryAdd(clonedType, cloner);
|
||||
}
|
||||
|
||||
return cloner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the existing clone method for the specified type or compiles one if
|
||||
/// none exists for the type yet
|
||||
/// </summary>
|
||||
/// <param name="clonedType">Type for which a clone method will be retrieved</param>
|
||||
/// <returns>The clone method for the specified type</returns>
|
||||
private static Func<object, object> getOrCreateDeepFieldBasedCloner(Type clonedType) {
|
||||
Func<object, object> cloner;
|
||||
|
||||
if(!deepFieldBasedCloners.TryGetValue(clonedType, out cloner)) {
|
||||
cloner = createDeepFieldBasedCloner(clonedType);
|
||||
deepFieldBasedCloners.TryAdd(clonedType, cloner);
|
||||
}
|
||||
|
||||
return cloner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the existing clone method for the specified type or compiles one if
|
||||
/// none exists for the type yet
|
||||
/// </summary>
|
||||
/// <param name="clonedType">Type for which a clone method will be retrieved</param>
|
||||
/// <returns>The clone method for the specified type</returns>
|
||||
private static Func<object, object> getOrCreateShallowPropertyBasedCloner(Type clonedType) {
|
||||
Func<object, object> cloner;
|
||||
|
||||
if(!shallowPropertyBasedCloners.TryGetValue(clonedType, out cloner)) {
|
||||
cloner = createShallowPropertyBasedCloner(clonedType);
|
||||
shallowPropertyBasedCloners.TryAdd(clonedType, cloner);
|
||||
}
|
||||
|
||||
return cloner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the existing clone method for the specified type or compiles one if
|
||||
/// none exists for the type yet
|
||||
/// </summary>
|
||||
/// <param name="clonedType">Type for which a clone method will be retrieved</param>
|
||||
/// <returns>The clone method for the specified type</returns>
|
||||
private static Func<object, object> getOrCreateDeepPropertyBasedCloner(Type clonedType) {
|
||||
Func<object, object> cloner;
|
||||
|
||||
if(!deepPropertyBasedCloners.TryGetValue(clonedType, out cloner)) {
|
||||
cloner = createDeepPropertyBasedCloner(clonedType);
|
||||
deepPropertyBasedCloners.TryAdd(clonedType, cloner);
|
||||
}
|
||||
|
||||
return cloner;
|
||||
}
|
||||
|
||||
/// <summary>Compiled cloners that perform shallow clone operations</summary>
|
||||
private static readonly ConcurrentDictionary<
|
||||
Type, Func<object, object>
|
||||
> shallowFieldBasedCloners;
|
||||
/// <summary>Compiled cloners that perform deep clone operations</summary>
|
||||
private static readonly ConcurrentDictionary<
|
||||
Type, Func<object, object>
|
||||
> deepFieldBasedCloners;
|
||||
/// <summary>Compiled cloners that perform shallow clone operations</summary>
|
||||
private static readonly ConcurrentDictionary<
|
||||
Type, Func<object, object>
|
||||
> shallowPropertyBasedCloners;
|
||||
/// <summary>Compiled cloners that perform deep clone operations</summary>
|
||||
private static readonly ConcurrentDictionary<
|
||||
Type, Func<object, object>
|
||||
> deepPropertyBasedCloners;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
||||
#endif // !NO_SETS
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_SETS
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>
|
||||
/// Cloning factory which uses expression trees to improve performance when cloning
|
||||
/// is a high-frequency action.
|
||||
/// </summary>
|
||||
public partial class ExpressionTreeCloner : ICloneFactory {
|
||||
|
||||
/// <summary>Initializes the static members of the expression tree cloner</summary>
|
||||
static ExpressionTreeCloner() {
|
||||
shallowFieldBasedCloners = new ConcurrentDictionary<Type, Func<object, object>>();
|
||||
deepFieldBasedCloners = new ConcurrentDictionary<Type, Func<object, object>>();
|
||||
shallowPropertyBasedCloners = new ConcurrentDictionary<Type, Func<object, object>>();
|
||||
deepPropertyBasedCloners = new ConcurrentDictionary<Type, Func<object, object>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToCloneAsObject == null) {
|
||||
return default(TCloned);
|
||||
}
|
||||
|
||||
Func<object, object> cloner = getOrCreateDeepFieldBasedCloner(typeof(TCloned));
|
||||
return (TCloned)cloner(objectToCloneAsObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToCloneAsObject == null) {
|
||||
return default(TCloned);
|
||||
}
|
||||
|
||||
Func<object, object> cloner = getOrCreateDeepPropertyBasedCloner(typeof(TCloned));
|
||||
return (TCloned)cloner(objectToCloneAsObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
public static TCloned ShallowFieldClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToCloneAsObject == null) {
|
||||
return default(TCloned);
|
||||
}
|
||||
|
||||
Func<object, object> cloner = getOrCreateShallowFieldBasedCloner(typeof(TCloned));
|
||||
return (TCloned)cloner(objectToCloneAsObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
public static TCloned ShallowPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToCloneAsObject == null) {
|
||||
return default(TCloned);
|
||||
}
|
||||
|
||||
Func<object, object> cloner = getOrCreateShallowPropertyBasedCloner(typeof(TCloned));
|
||||
return (TCloned)cloner(objectToCloneAsObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowFieldClone<TCloned>(TCloned objectToClone) {
|
||||
return ExpressionTreeCloner.ShallowFieldClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
return ExpressionTreeCloner.ShallowPropertyClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
return ExpressionTreeCloner.DeepFieldClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
return ExpressionTreeCloner.DeepPropertyClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// Transfers the state of one object into another, creating clones of referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
public void DeepCopyState<TState>(TState original, TState target, bool propertyBased)
|
||||
where TState : class {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transfers the state of one object into another, creating clones of referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
public void DeepCopyState<TState>(ref TState original, ref TState target, bool propertyBased)
|
||||
where TState : struct {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>Transfers the state of one object into another</summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
public void ShallowCopyState<TState>(TState original, TState target, bool propertyBased)
|
||||
where TState : class {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>Transfers the state of one object into another</summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
public void ShallowCopyState<TState>(ref TState original, ref TState target, bool propertyBased)
|
||||
where TState : struct {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the existing clone method for the specified type or compiles one if
|
||||
/// none exists for the type yet
|
||||
/// </summary>
|
||||
/// <param name="clonedType">Type for which a clone method will be retrieved</param>
|
||||
/// <returns>The clone method for the specified type</returns>
|
||||
private static Func<object, object> getOrCreateShallowFieldBasedCloner(Type clonedType) {
|
||||
Func<object, object> cloner;
|
||||
|
||||
if(!shallowFieldBasedCloners.TryGetValue(clonedType, out cloner)) {
|
||||
cloner = createShallowFieldBasedCloner(clonedType);
|
||||
shallowFieldBasedCloners.TryAdd(clonedType, cloner);
|
||||
}
|
||||
|
||||
return cloner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the existing clone method for the specified type or compiles one if
|
||||
/// none exists for the type yet
|
||||
/// </summary>
|
||||
/// <param name="clonedType">Type for which a clone method will be retrieved</param>
|
||||
/// <returns>The clone method for the specified type</returns>
|
||||
private static Func<object, object> getOrCreateDeepFieldBasedCloner(Type clonedType) {
|
||||
Func<object, object> cloner;
|
||||
|
||||
if(!deepFieldBasedCloners.TryGetValue(clonedType, out cloner)) {
|
||||
cloner = createDeepFieldBasedCloner(clonedType);
|
||||
deepFieldBasedCloners.TryAdd(clonedType, cloner);
|
||||
}
|
||||
|
||||
return cloner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the existing clone method for the specified type or compiles one if
|
||||
/// none exists for the type yet
|
||||
/// </summary>
|
||||
/// <param name="clonedType">Type for which a clone method will be retrieved</param>
|
||||
/// <returns>The clone method for the specified type</returns>
|
||||
private static Func<object, object> getOrCreateShallowPropertyBasedCloner(Type clonedType) {
|
||||
Func<object, object> cloner;
|
||||
|
||||
if(!shallowPropertyBasedCloners.TryGetValue(clonedType, out cloner)) {
|
||||
cloner = createShallowPropertyBasedCloner(clonedType);
|
||||
shallowPropertyBasedCloners.TryAdd(clonedType, cloner);
|
||||
}
|
||||
|
||||
return cloner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the existing clone method for the specified type or compiles one if
|
||||
/// none exists for the type yet
|
||||
/// </summary>
|
||||
/// <param name="clonedType">Type for which a clone method will be retrieved</param>
|
||||
/// <returns>The clone method for the specified type</returns>
|
||||
private static Func<object, object> getOrCreateDeepPropertyBasedCloner(Type clonedType) {
|
||||
Func<object, object> cloner;
|
||||
|
||||
if(!deepPropertyBasedCloners.TryGetValue(clonedType, out cloner)) {
|
||||
cloner = createDeepPropertyBasedCloner(clonedType);
|
||||
deepPropertyBasedCloners.TryAdd(clonedType, cloner);
|
||||
}
|
||||
|
||||
return cloner;
|
||||
}
|
||||
|
||||
/// <summary>Compiled cloners that perform shallow clone operations</summary>
|
||||
private static readonly ConcurrentDictionary<
|
||||
Type, Func<object, object>
|
||||
> shallowFieldBasedCloners;
|
||||
/// <summary>Compiled cloners that perform deep clone operations</summary>
|
||||
private static readonly ConcurrentDictionary<
|
||||
Type, Func<object, object>
|
||||
> deepFieldBasedCloners;
|
||||
/// <summary>Compiled cloners that perform shallow clone operations</summary>
|
||||
private static readonly ConcurrentDictionary<
|
||||
Type, Func<object, object>
|
||||
> shallowPropertyBasedCloners;
|
||||
/// <summary>Compiled cloners that perform deep clone operations</summary>
|
||||
private static readonly ConcurrentDictionary<
|
||||
Type, Func<object, object>
|
||||
> deepPropertyBasedCloners;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
||||
#endif // !NO_SETS
|
||||
|
|
|
@ -1,100 +1,99 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 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;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Constructs new objects by cloning existing objects</summary>
|
||||
public interface ICloneFactory {
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
/// <remarks>
|
||||
/// Field-based clones are guaranteed to be complete - there will be no missed
|
||||
/// members. This type of clone is also able to clone types that do not provide
|
||||
/// a default constructor.
|
||||
/// </remarks>
|
||||
TCloned ShallowFieldClone<TCloned>(TCloned objectToClone);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A property-based clone is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based clone, internal proxy fields would be cloned
|
||||
/// and might cause problems with the ORM.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Property-based clones require a default constructor because there's no guarantee
|
||||
/// that all fields will are assignable through properties and starting with
|
||||
/// an uninitialized object is likely to end up with a broken clone.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
TCloned ShallowPropertyClone<TCloned>(TCloned objectToClone);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
/// <remarks>
|
||||
/// Field-based clones are guaranteed to be complete - there will be no missed
|
||||
/// members. This type of clone is also able to clone types that do not provide
|
||||
/// a default constructor.
|
||||
/// </remarks>
|
||||
TCloned DeepFieldClone<TCloned>(TCloned objectToClone);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A property-based clone is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based clone, internal proxy fields would be cloned
|
||||
/// and might cause problems with the ORM.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Property-based clones require a default constructor because there's no guarantee
|
||||
/// that all fields will are assignable through properties and starting with
|
||||
/// an uninitialized object is likely to end up with a broken clone.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
TCloned DeepPropertyClone<TCloned>(TCloned objectToClone);
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Constructs new objects by cloning existing objects</summary>
|
||||
public interface ICloneFactory {
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
/// <remarks>
|
||||
/// Field-based clones are guaranteed to be complete - there will be no missed
|
||||
/// members. This type of clone is also able to clone types that do not provide
|
||||
/// a default constructor.
|
||||
/// </remarks>
|
||||
TCloned ShallowFieldClone<TCloned>(TCloned objectToClone);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A property-based clone is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based clone, internal proxy fields would be cloned
|
||||
/// and might cause problems with the ORM.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Property-based clones require a default constructor because there's no guarantee
|
||||
/// that all fields will are assignable through properties and starting with
|
||||
/// an uninitialized object is likely to end up with a broken clone.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
TCloned ShallowPropertyClone<TCloned>(TCloned objectToClone);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
/// <remarks>
|
||||
/// Field-based clones are guaranteed to be complete - there will be no missed
|
||||
/// members. This type of clone is also able to clone types that do not provide
|
||||
/// a default constructor.
|
||||
/// </remarks>
|
||||
TCloned DeepFieldClone<TCloned>(TCloned objectToClone);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A property-based clone is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based clone, internal proxy fields would be cloned
|
||||
/// and might cause problems with the ORM.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Property-based clones require a default constructor because there's no guarantee
|
||||
/// that all fields will are assignable through properties and starting with
|
||||
/// an uninitialized object is likely to end up with a broken clone.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
TCloned DeepPropertyClone<TCloned>(TCloned objectToClone);
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
|
|
@ -1,90 +1,89 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 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;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Copies the state of objects</summary>
|
||||
public interface IStateCopier {
|
||||
|
||||
/// <summary>
|
||||
/// Transfers the state of one object into another, creating clones of referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
/// <remarks>
|
||||
/// A property-based copy is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based copy, internal proxy fields would be copied
|
||||
/// and might cause problems with the ORM.
|
||||
/// </remarks>
|
||||
void DeepCopyState<TState>(TState original, TState target, bool propertyBased)
|
||||
where TState : class;
|
||||
|
||||
/// <summary>
|
||||
/// Transfers the state of one object into another, creating clones of referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
/// <remarks>
|
||||
/// A property-based copy is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based copy, internal proxy fields would be copied
|
||||
/// and might cause problems with the ORM.
|
||||
/// </remarks>
|
||||
void DeepCopyState<TState>(ref TState original, ref TState target, bool propertyBased)
|
||||
where TState : struct;
|
||||
|
||||
/// <summary>Transfers the state of one object into another</summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
/// <remarks>
|
||||
/// A property-based copy is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based copy, internal proxy fields would be copied
|
||||
/// and might cause problems with the ORM.
|
||||
/// </remarks>
|
||||
void ShallowCopyState<TState>(TState original, TState target, bool propertyBased)
|
||||
where TState : class;
|
||||
|
||||
/// <summary>Transfers the state of one object into another</summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
/// <remarks>
|
||||
/// A property-based copy is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based copy, internal proxy fields would be copied
|
||||
/// and might cause problems with the ORM.
|
||||
/// </remarks>
|
||||
void ShallowCopyState<TState>(ref TState original, ref TState target, bool propertyBased)
|
||||
where TState : struct;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Copies the state of objects</summary>
|
||||
public interface IStateCopier {
|
||||
|
||||
/// <summary>
|
||||
/// Transfers the state of one object into another, creating clones of referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
/// <remarks>
|
||||
/// A property-based copy is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based copy, internal proxy fields would be copied
|
||||
/// and might cause problems with the ORM.
|
||||
/// </remarks>
|
||||
void DeepCopyState<TState>(TState original, TState target, bool propertyBased)
|
||||
where TState : class;
|
||||
|
||||
/// <summary>
|
||||
/// Transfers the state of one object into another, creating clones of referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
/// <remarks>
|
||||
/// A property-based copy is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based copy, internal proxy fields would be copied
|
||||
/// and might cause problems with the ORM.
|
||||
/// </remarks>
|
||||
void DeepCopyState<TState>(ref TState original, ref TState target, bool propertyBased)
|
||||
where TState : struct;
|
||||
|
||||
/// <summary>Transfers the state of one object into another</summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
/// <remarks>
|
||||
/// A property-based copy is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based copy, internal proxy fields would be copied
|
||||
/// and might cause problems with the ORM.
|
||||
/// </remarks>
|
||||
void ShallowCopyState<TState>(TState original, TState target, bool propertyBased)
|
||||
where TState : class;
|
||||
|
||||
/// <summary>Transfers the state of one object into another</summary>
|
||||
/// <typeparam name="TState">Type of the object whose sate will be transferred</typeparam>
|
||||
/// <param name="original">Original instance the state will be taken from</param>
|
||||
/// <param name="target">Target instance the state will be written to</param>
|
||||
/// <param name="propertyBased">Whether to perform a property-based state copy</param>
|
||||
/// <remarks>
|
||||
/// A property-based copy is useful if you're using dynamically generated proxies,
|
||||
/// such as when working with entities returned by an ORM like NHibernate.
|
||||
/// When not using a property-based copy, internal proxy fields would be copied
|
||||
/// and might cause problems with the ORM.
|
||||
/// </remarks>
|
||||
void ShallowCopyState<TState>(ref TState original, ref TState target, bool propertyBased)
|
||||
where TState : struct;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
|
|
@ -1,199 +1,198 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 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 {
|
||||
|
||||
/// <summary>Unit Test for the reflection-based cloner</summary>
|
||||
[TestFixture]
|
||||
internal class ReflectionClonerTest : CloneFactoryTest {
|
||||
|
||||
/// <summary>Initializes a new unit test suite for the reflection cloner</summary>
|
||||
public ReflectionClonerTest() {
|
||||
this.cloneFactory = new ReflectionCloner();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that cloning a null object simply returns null</summary>
|
||||
[Test]
|
||||
public void CloningNullYieldsNull() {
|
||||
Assert.IsNull(this.cloneFactory.DeepFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.DeepPropertyClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowPropertyClone<object>(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that clones of objects whose class doesn't possess a default constructor
|
||||
/// can be made
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ClassWithoutDefaultConstructorCanBeCloned() {
|
||||
var original = new ClassWithoutDefaultConstructor(1234);
|
||||
ClassWithoutDefaultConstructor clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.Dummy, clone.Dummy);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clones of primitive types can be created</summary>
|
||||
[Test]
|
||||
public void PrimitiveTypesCanBeCloned() {
|
||||
int original = 12345;
|
||||
int clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
Assert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void ShallowClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
|
||||
Assert.AreSame(original[0], clone[0]);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void DeepClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original[0], clone[0]);
|
||||
Assert.AreEqual(original[0].TestField, clone[0].TestField);
|
||||
Assert.AreEqual(original[0].TestProperty, clone[0].TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic list can be made</summary>
|
||||
[Test]
|
||||
public void GenericListsCanBeCloned() {
|
||||
var original = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
List<int> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic dictionary can be made</summary>
|
||||
[Test]
|
||||
public void GenericDictionariesCanBeCloned() {
|
||||
var original = new Dictionary<int, string>();
|
||||
original.Add(1, "one");
|
||||
Dictionary<int, string> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreEqual("one", clone[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>Clone factory being tested</summary>
|
||||
private ICloneFactory cloneFactory;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
||||
#endif // UNITTEST
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Unit Test for the reflection-based cloner</summary>
|
||||
[TestFixture]
|
||||
internal class ReflectionClonerTest : CloneFactoryTest {
|
||||
|
||||
/// <summary>Initializes a new unit test suite for the reflection cloner</summary>
|
||||
public ReflectionClonerTest() {
|
||||
this.cloneFactory = new ReflectionCloner();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that cloning a null object simply returns null</summary>
|
||||
[Test]
|
||||
public void CloningNullYieldsNull() {
|
||||
Assert.IsNull(this.cloneFactory.DeepFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.DeepPropertyClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowPropertyClone<object>(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that clones of objects whose class doesn't possess a default constructor
|
||||
/// can be made
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ClassWithoutDefaultConstructorCanBeCloned() {
|
||||
var original = new ClassWithoutDefaultConstructor(1234);
|
||||
ClassWithoutDefaultConstructor clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.Dummy, clone.Dummy);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clones of primitive types can be created</summary>
|
||||
[Test]
|
||||
public void PrimitiveTypesCanBeCloned() {
|
||||
int original = 12345;
|
||||
int clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
Assert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void ShallowClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
|
||||
Assert.AreSame(original[0], clone[0]);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void DeepClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original[0], clone[0]);
|
||||
Assert.AreEqual(original[0].TestField, clone[0].TestField);
|
||||
Assert.AreEqual(original[0].TestProperty, clone[0].TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic list can be made</summary>
|
||||
[Test]
|
||||
public void GenericListsCanBeCloned() {
|
||||
var original = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
List<int> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic dictionary can be made</summary>
|
||||
[Test]
|
||||
public void GenericDictionariesCanBeCloned() {
|
||||
var original = new Dictionary<int, string>();
|
||||
original.Add(1, "one");
|
||||
Dictionary<int, string> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreEqual("one", clone[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>Clone factory being tested</summary>
|
||||
private ICloneFactory cloneFactory;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
|
@ -1,451 +1,450 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 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.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Clones objects using reflection</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// 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.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class ReflectionCloner : ICloneFactory {
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
public static TCloned ShallowFieldClone<TCloned>(TCloned objectToClone) {
|
||||
Type originalType = objectToClone.GetType();
|
||||
if(originalType.IsPrimitive || (originalType == typeof(string))) {
|
||||
return objectToClone; // Being value types, primitives are copied by default
|
||||
} else if(originalType.IsArray) {
|
||||
return (TCloned)shallowCloneArray(objectToClone);
|
||||
} else if(originalType.IsValueType) {
|
||||
return objectToClone; // Value types can be copied directly
|
||||
} else {
|
||||
return (TCloned)shallowCloneComplexFieldBased(objectToClone);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
public static TCloned ShallowPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
Type originalType = objectToClone.GetType();
|
||||
if(originalType.IsPrimitive || (originalType == typeof(string))) {
|
||||
return objectToClone; // Being value types, primitives are copied by default
|
||||
} else if(originalType.IsArray) {
|
||||
return (TCloned)shallowCloneArray(objectToClone);
|
||||
} else if(originalType.IsValueType) {
|
||||
return (TCloned)shallowCloneComplexPropertyBased(objectToClone);
|
||||
} else {
|
||||
return (TCloned)shallowCloneComplexPropertyBased(objectToClone);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToClone == null) {
|
||||
return default(TCloned);
|
||||
} else {
|
||||
return (TCloned)deepCloneSingleFieldBased(objectToCloneAsObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToClone == null) {
|
||||
return default(TCloned);
|
||||
} else {
|
||||
return (TCloned)deepCloneSinglePropertyBased(objectToCloneAsObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowFieldClone<TCloned>(TCloned objectToClone) {
|
||||
if(typeof(TCloned).IsClass || typeof(TCloned).IsArray) {
|
||||
if(ReferenceEquals(objectToClone, null)) {
|
||||
return default(TCloned);
|
||||
}
|
||||
}
|
||||
return ReflectionCloner.ShallowFieldClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
if(typeof(TCloned).IsClass || typeof(TCloned).IsArray) {
|
||||
if(ReferenceEquals(objectToClone, null)) {
|
||||
return default(TCloned);
|
||||
}
|
||||
}
|
||||
return ReflectionCloner.ShallowPropertyClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
return ReflectionCloner.DeepFieldClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
return ReflectionCloner.DeepPropertyClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>Clones a complex type using field-based value transfer</summary>
|
||||
/// <param name="original">Original instance that will be cloned</param>
|
||||
/// <returns>A clone of the original instance</returns>
|
||||
private static object shallowCloneComplexFieldBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
object clone = FormatterServices.GetUninitializedObject(originalType);
|
||||
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
object originalValue = fieldInfo.GetValue(original);
|
||||
if(originalValue != null) {
|
||||
// Everything's just directly assigned in a shallow clone
|
||||
fieldInfo.SetValue(clone, originalValue);
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>Clones a complex type using property-based value transfer</summary>
|
||||
/// <param name="original">Original instance that will be cloned</param>
|
||||
/// <returns>A clone of the original instance</returns>
|
||||
private static object shallowCloneComplexPropertyBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
object clone = Activator.CreateInstance(originalType);
|
||||
|
||||
PropertyInfo[] propertyInfos = originalType.GetProperties(
|
||||
BindingFlags.Public | BindingFlags.NonPublic |
|
||||
BindingFlags.Instance | BindingFlags.FlattenHierarchy
|
||||
);
|
||||
for(int index = 0; index < propertyInfos.Length; ++index) {
|
||||
PropertyInfo propertyInfo = propertyInfos[index];
|
||||
if(propertyInfo.CanRead && propertyInfo.CanWrite) {
|
||||
Type propertyType = propertyInfo.PropertyType;
|
||||
object originalValue = propertyInfo.GetValue(original, null);
|
||||
if(originalValue != null) {
|
||||
if(propertyType.IsPrimitive || (propertyType == typeof(string))) {
|
||||
// Primitive types can be assigned directly
|
||||
propertyInfo.SetValue(clone, originalValue, null);
|
||||
} else if(propertyType.IsValueType) {
|
||||
// Value types are seen as part of the original type and are thus recursed into
|
||||
propertyInfo.SetValue(clone, shallowCloneComplexPropertyBased(originalValue), null);
|
||||
} else if(propertyType.IsArray) { // Arrays are assigned directly in a shallow clone
|
||||
propertyInfo.SetValue(clone, originalValue, null);
|
||||
} else { // Complex types are directly assigned without creating a copy
|
||||
propertyInfo.SetValue(clone, originalValue, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>Clones an array using field-based value transfer</summary>
|
||||
/// <param name="original">Original array that will be cloned</param>
|
||||
/// <returns>A clone of the original array</returns>
|
||||
private static object shallowCloneArray(object original) {
|
||||
return ((Array)original).Clone();
|
||||
}
|
||||
|
||||
/// <summary>Copies a single object using field-based value transfer</summary>
|
||||
/// <param name="original">Original object that will be cloned</param>
|
||||
/// <returns>A clone of the original object</returns>
|
||||
private static object deepCloneSingleFieldBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
if(originalType.IsPrimitive || (originalType == typeof(string))) {
|
||||
return original; // Creates another box, does not reference boxed primitive
|
||||
} else if(originalType.IsArray) {
|
||||
return deepCloneArrayFieldBased((Array)original, originalType.GetElementType());
|
||||
} else {
|
||||
return deepCloneComplexFieldBased(original);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Clones a complex type using field-based value transfer</summary>
|
||||
/// <param name="original">Original instance that will be cloned</param>
|
||||
/// <returns>A clone of the original instance</returns>
|
||||
private static object deepCloneComplexFieldBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
object clone = FormatterServices.GetUninitializedObject(originalType);
|
||||
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
Type fieldType = fieldInfo.FieldType;
|
||||
object originalValue = fieldInfo.GetValue(original);
|
||||
if(originalValue != null) {
|
||||
// Primitive types can be assigned directly
|
||||
if(fieldType.IsPrimitive || (fieldType == typeof(string))) {
|
||||
fieldInfo.SetValue(clone, originalValue);
|
||||
} else if(fieldType.IsArray) { // Arrays need to be cloned element-by-element
|
||||
fieldInfo.SetValue(
|
||||
clone,
|
||||
deepCloneArrayFieldBased((Array)originalValue, fieldType.GetElementType())
|
||||
);
|
||||
} else { // Complex types need to be cloned member-by-member
|
||||
fieldInfo.SetValue(clone, deepCloneSingleFieldBased(originalValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>Clones an array using field-based value transfer</summary>
|
||||
/// <param name="original">Original array that will be cloned</param>
|
||||
/// <param name="elementType">Type of elements the original array contains</param>
|
||||
/// <returns>A clone of the original array</returns>
|
||||
private static object deepCloneArrayFieldBased(Array original, Type elementType) {
|
||||
if(elementType.IsPrimitive || (elementType == typeof(string))) {
|
||||
return original.Clone();
|
||||
}
|
||||
|
||||
int dimensionCount = original.Rank;
|
||||
|
||||
// Find out the length of each of the array's dimensions, also calculate how
|
||||
// many elements there are in the array in total.
|
||||
var lengths = new int[dimensionCount];
|
||||
int totalElementCount = 0;
|
||||
for(int index = 0; index < dimensionCount; ++index) {
|
||||
lengths[index] = original.GetLength(index);
|
||||
if(index == 0) {
|
||||
totalElementCount = lengths[index];
|
||||
} else {
|
||||
totalElementCount *= lengths[index];
|
||||
}
|
||||
}
|
||||
|
||||
// Knowing the number of dimensions and the length of each dimension, we can
|
||||
// create another array of the exact same sizes.
|
||||
Array clone = Array.CreateInstance(elementType, lengths);
|
||||
|
||||
// If this is a one-dimensional array (most common type), do an optimized copy
|
||||
// directly specifying the indices
|
||||
if(dimensionCount == 1) {
|
||||
|
||||
// Clone each element of the array directly
|
||||
for(int index = 0; index < totalElementCount; ++index) {
|
||||
object originalElement = original.GetValue(index);
|
||||
if(originalElement != null) {
|
||||
clone.SetValue(deepCloneSingleFieldBased(originalElement), index);
|
||||
}
|
||||
}
|
||||
|
||||
} else { // Otherwise use the generic code for multi-dimensional arrays
|
||||
|
||||
var indices = new int[dimensionCount];
|
||||
for(int index = 0; index < totalElementCount; ++index) {
|
||||
|
||||
// Determine the index for each of the array's dimensions
|
||||
int elementIndex = index;
|
||||
for(int dimensionIndex = dimensionCount - 1; dimensionIndex >= 0; --dimensionIndex) {
|
||||
indices[dimensionIndex] = elementIndex % lengths[dimensionIndex];
|
||||
elementIndex /= lengths[dimensionIndex];
|
||||
}
|
||||
|
||||
// Clone the current array element
|
||||
object originalElement = original.GetValue(indices);
|
||||
if(originalElement != null) {
|
||||
clone.SetValue(deepCloneSingleFieldBased(originalElement), indices);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>Copies a single object using property-based value transfer</summary>
|
||||
/// <param name="original">Original object that will be cloned</param>
|
||||
/// <returns>A clone of the original object</returns>
|
||||
private static object deepCloneSinglePropertyBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
if(originalType.IsPrimitive || (originalType == typeof(string))) {
|
||||
return original; // Creates another box, does not reference boxed primitive
|
||||
} else if(originalType.IsArray) {
|
||||
return deepCloneArrayPropertyBased((Array)original, originalType.GetElementType());
|
||||
} else {
|
||||
return deepCloneComplexPropertyBased(original);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Clones a complex type using property-based value transfer</summary>
|
||||
/// <param name="original">Original instance that will be cloned</param>
|
||||
/// <returns>A clone of the original instance</returns>
|
||||
private static object deepCloneComplexPropertyBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
object clone = Activator.CreateInstance(originalType);
|
||||
|
||||
PropertyInfo[] propertyInfos = originalType.GetProperties(
|
||||
BindingFlags.Public | BindingFlags.NonPublic |
|
||||
BindingFlags.Instance | BindingFlags.FlattenHierarchy
|
||||
);
|
||||
for(int index = 0; index < propertyInfos.Length; ++index) {
|
||||
PropertyInfo propertyInfo = propertyInfos[index];
|
||||
if(propertyInfo.CanRead && propertyInfo.CanWrite) {
|
||||
Type propertyType = propertyInfo.PropertyType;
|
||||
object originalValue = propertyInfo.GetValue(original, null);
|
||||
if(originalValue != null) {
|
||||
if(propertyType.IsPrimitive || (propertyType == typeof(string))) {
|
||||
// Primitive types can be assigned directly
|
||||
propertyInfo.SetValue(clone, originalValue, null);
|
||||
} else if(propertyType.IsArray) { // Arrays need to be cloned element-by-element
|
||||
propertyInfo.SetValue(
|
||||
clone,
|
||||
deepCloneArrayPropertyBased((Array)originalValue, propertyType.GetElementType()),
|
||||
null
|
||||
);
|
||||
} else { // Complex types need to be cloned member-by-member
|
||||
propertyInfo.SetValue(clone, deepCloneSinglePropertyBased(originalValue), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>Clones an array using property-based value transfer</summary>
|
||||
/// <param name="original">Original array that will be cloned</param>
|
||||
/// <param name="elementType">Type of elements the original array contains</param>
|
||||
/// <returns>A clone of the original array</returns>
|
||||
private static object deepCloneArrayPropertyBased(Array original, Type elementType) {
|
||||
if(elementType.IsPrimitive || (elementType == typeof(string))) {
|
||||
return original.Clone();
|
||||
}
|
||||
|
||||
int dimensionCount = original.Rank;
|
||||
|
||||
// Find out the length of each of the array's dimensions, also calculate how
|
||||
// many elements there are in the array in total.
|
||||
var lengths = new int[dimensionCount];
|
||||
int totalElementCount = 0;
|
||||
for(int index = 0; index < dimensionCount; ++index) {
|
||||
lengths[index] = original.GetLength(index);
|
||||
if(index == 0) {
|
||||
totalElementCount = lengths[index];
|
||||
} else {
|
||||
totalElementCount *= lengths[index];
|
||||
}
|
||||
}
|
||||
|
||||
// Knowing the number of dimensions and the length of each dimension, we can
|
||||
// create another array of the exact same sizes.
|
||||
Array clone = Array.CreateInstance(elementType, lengths);
|
||||
|
||||
// If this is a one-dimensional array (most common type), do an optimized copy
|
||||
// directly specifying the indices
|
||||
if(dimensionCount == 1) {
|
||||
|
||||
// Clone each element of the array directly
|
||||
for(int index = 0; index < totalElementCount; ++index) {
|
||||
object originalElement = original.GetValue(index);
|
||||
if(originalElement != null) {
|
||||
clone.SetValue(deepCloneSinglePropertyBased(originalElement), index);
|
||||
}
|
||||
}
|
||||
|
||||
} else { // Otherwise use the generic code for multi-dimensional arrays
|
||||
|
||||
var indices = new int[dimensionCount];
|
||||
for(int index = 0; index < totalElementCount; ++index) {
|
||||
|
||||
// Determine the index for each of the array's dimensions
|
||||
int elementIndex = index;
|
||||
for(int dimensionIndex = dimensionCount - 1; dimensionIndex >= 0; --dimensionIndex) {
|
||||
indices[dimensionIndex] = elementIndex % lengths[dimensionIndex];
|
||||
elementIndex /= lengths[dimensionIndex];
|
||||
}
|
||||
|
||||
// Clone the current array element
|
||||
object originalElement = original.GetValue(indices);
|
||||
if(originalElement != null) {
|
||||
clone.SetValue(deepCloneSinglePropertyBased(originalElement), indices);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Clones objects using reflection</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// 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.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class ReflectionCloner : ICloneFactory {
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
public static TCloned ShallowFieldClone<TCloned>(TCloned objectToClone) {
|
||||
Type originalType = objectToClone.GetType();
|
||||
if(originalType.IsPrimitive || (originalType == typeof(string))) {
|
||||
return objectToClone; // Being value types, primitives are copied by default
|
||||
} else if(originalType.IsArray) {
|
||||
return (TCloned)shallowCloneArray(objectToClone);
|
||||
} else if(originalType.IsValueType) {
|
||||
return objectToClone; // Value types can be copied directly
|
||||
} else {
|
||||
return (TCloned)shallowCloneComplexFieldBased(objectToClone);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
public static TCloned ShallowPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
Type originalType = objectToClone.GetType();
|
||||
if(originalType.IsPrimitive || (originalType == typeof(string))) {
|
||||
return objectToClone; // Being value types, primitives are copied by default
|
||||
} else if(originalType.IsArray) {
|
||||
return (TCloned)shallowCloneArray(objectToClone);
|
||||
} else if(originalType.IsValueType) {
|
||||
return (TCloned)shallowCloneComplexPropertyBased(objectToClone);
|
||||
} else {
|
||||
return (TCloned)shallowCloneComplexPropertyBased(objectToClone);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToClone == null) {
|
||||
return default(TCloned);
|
||||
} else {
|
||||
return (TCloned)deepCloneSingleFieldBased(objectToCloneAsObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
object objectToCloneAsObject = objectToClone;
|
||||
if(objectToClone == null) {
|
||||
return default(TCloned);
|
||||
} else {
|
||||
return (TCloned)deepCloneSinglePropertyBased(objectToCloneAsObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowFieldClone<TCloned>(TCloned objectToClone) {
|
||||
if(typeof(TCloned).IsClass || typeof(TCloned).IsArray) {
|
||||
if(ReferenceEquals(objectToClone, null)) {
|
||||
return default(TCloned);
|
||||
}
|
||||
}
|
||||
return ReflectionCloner.ShallowFieldClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
if(typeof(TCloned).IsClass || typeof(TCloned).IsArray) {
|
||||
if(ReferenceEquals(objectToClone, null)) {
|
||||
return default(TCloned);
|
||||
}
|
||||
}
|
||||
return ReflectionCloner.ShallowPropertyClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
return ReflectionCloner.DeepFieldClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
return ReflectionCloner.DeepPropertyClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>Clones a complex type using field-based value transfer</summary>
|
||||
/// <param name="original">Original instance that will be cloned</param>
|
||||
/// <returns>A clone of the original instance</returns>
|
||||
private static object shallowCloneComplexFieldBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
object clone = FormatterServices.GetUninitializedObject(originalType);
|
||||
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
object originalValue = fieldInfo.GetValue(original);
|
||||
if(originalValue != null) {
|
||||
// Everything's just directly assigned in a shallow clone
|
||||
fieldInfo.SetValue(clone, originalValue);
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>Clones a complex type using property-based value transfer</summary>
|
||||
/// <param name="original">Original instance that will be cloned</param>
|
||||
/// <returns>A clone of the original instance</returns>
|
||||
private static object shallowCloneComplexPropertyBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
object clone = Activator.CreateInstance(originalType);
|
||||
|
||||
PropertyInfo[] propertyInfos = originalType.GetProperties(
|
||||
BindingFlags.Public | BindingFlags.NonPublic |
|
||||
BindingFlags.Instance | BindingFlags.FlattenHierarchy
|
||||
);
|
||||
for(int index = 0; index < propertyInfos.Length; ++index) {
|
||||
PropertyInfo propertyInfo = propertyInfos[index];
|
||||
if(propertyInfo.CanRead && propertyInfo.CanWrite) {
|
||||
Type propertyType = propertyInfo.PropertyType;
|
||||
object originalValue = propertyInfo.GetValue(original, null);
|
||||
if(originalValue != null) {
|
||||
if(propertyType.IsPrimitive || (propertyType == typeof(string))) {
|
||||
// Primitive types can be assigned directly
|
||||
propertyInfo.SetValue(clone, originalValue, null);
|
||||
} else if(propertyType.IsValueType) {
|
||||
// Value types are seen as part of the original type and are thus recursed into
|
||||
propertyInfo.SetValue(clone, shallowCloneComplexPropertyBased(originalValue), null);
|
||||
} else if(propertyType.IsArray) { // Arrays are assigned directly in a shallow clone
|
||||
propertyInfo.SetValue(clone, originalValue, null);
|
||||
} else { // Complex types are directly assigned without creating a copy
|
||||
propertyInfo.SetValue(clone, originalValue, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>Clones an array using field-based value transfer</summary>
|
||||
/// <param name="original">Original array that will be cloned</param>
|
||||
/// <returns>A clone of the original array</returns>
|
||||
private static object shallowCloneArray(object original) {
|
||||
return ((Array)original).Clone();
|
||||
}
|
||||
|
||||
/// <summary>Copies a single object using field-based value transfer</summary>
|
||||
/// <param name="original">Original object that will be cloned</param>
|
||||
/// <returns>A clone of the original object</returns>
|
||||
private static object deepCloneSingleFieldBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
if(originalType.IsPrimitive || (originalType == typeof(string))) {
|
||||
return original; // Creates another box, does not reference boxed primitive
|
||||
} else if(originalType.IsArray) {
|
||||
return deepCloneArrayFieldBased((Array)original, originalType.GetElementType());
|
||||
} else {
|
||||
return deepCloneComplexFieldBased(original);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Clones a complex type using field-based value transfer</summary>
|
||||
/// <param name="original">Original instance that will be cloned</param>
|
||||
/// <returns>A clone of the original instance</returns>
|
||||
private static object deepCloneComplexFieldBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
object clone = FormatterServices.GetUninitializedObject(originalType);
|
||||
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
Type fieldType = fieldInfo.FieldType;
|
||||
object originalValue = fieldInfo.GetValue(original);
|
||||
if(originalValue != null) {
|
||||
// Primitive types can be assigned directly
|
||||
if(fieldType.IsPrimitive || (fieldType == typeof(string))) {
|
||||
fieldInfo.SetValue(clone, originalValue);
|
||||
} else if(fieldType.IsArray) { // Arrays need to be cloned element-by-element
|
||||
fieldInfo.SetValue(
|
||||
clone,
|
||||
deepCloneArrayFieldBased((Array)originalValue, fieldType.GetElementType())
|
||||
);
|
||||
} else { // Complex types need to be cloned member-by-member
|
||||
fieldInfo.SetValue(clone, deepCloneSingleFieldBased(originalValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>Clones an array using field-based value transfer</summary>
|
||||
/// <param name="original">Original array that will be cloned</param>
|
||||
/// <param name="elementType">Type of elements the original array contains</param>
|
||||
/// <returns>A clone of the original array</returns>
|
||||
private static object deepCloneArrayFieldBased(Array original, Type elementType) {
|
||||
if(elementType.IsPrimitive || (elementType == typeof(string))) {
|
||||
return original.Clone();
|
||||
}
|
||||
|
||||
int dimensionCount = original.Rank;
|
||||
|
||||
// Find out the length of each of the array's dimensions, also calculate how
|
||||
// many elements there are in the array in total.
|
||||
var lengths = new int[dimensionCount];
|
||||
int totalElementCount = 0;
|
||||
for(int index = 0; index < dimensionCount; ++index) {
|
||||
lengths[index] = original.GetLength(index);
|
||||
if(index == 0) {
|
||||
totalElementCount = lengths[index];
|
||||
} else {
|
||||
totalElementCount *= lengths[index];
|
||||
}
|
||||
}
|
||||
|
||||
// Knowing the number of dimensions and the length of each dimension, we can
|
||||
// create another array of the exact same sizes.
|
||||
Array clone = Array.CreateInstance(elementType, lengths);
|
||||
|
||||
// If this is a one-dimensional array (most common type), do an optimized copy
|
||||
// directly specifying the indices
|
||||
if(dimensionCount == 1) {
|
||||
|
||||
// Clone each element of the array directly
|
||||
for(int index = 0; index < totalElementCount; ++index) {
|
||||
object originalElement = original.GetValue(index);
|
||||
if(originalElement != null) {
|
||||
clone.SetValue(deepCloneSingleFieldBased(originalElement), index);
|
||||
}
|
||||
}
|
||||
|
||||
} else { // Otherwise use the generic code for multi-dimensional arrays
|
||||
|
||||
var indices = new int[dimensionCount];
|
||||
for(int index = 0; index < totalElementCount; ++index) {
|
||||
|
||||
// Determine the index for each of the array's dimensions
|
||||
int elementIndex = index;
|
||||
for(int dimensionIndex = dimensionCount - 1; dimensionIndex >= 0; --dimensionIndex) {
|
||||
indices[dimensionIndex] = elementIndex % lengths[dimensionIndex];
|
||||
elementIndex /= lengths[dimensionIndex];
|
||||
}
|
||||
|
||||
// Clone the current array element
|
||||
object originalElement = original.GetValue(indices);
|
||||
if(originalElement != null) {
|
||||
clone.SetValue(deepCloneSingleFieldBased(originalElement), indices);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>Copies a single object using property-based value transfer</summary>
|
||||
/// <param name="original">Original object that will be cloned</param>
|
||||
/// <returns>A clone of the original object</returns>
|
||||
private static object deepCloneSinglePropertyBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
if(originalType.IsPrimitive || (originalType == typeof(string))) {
|
||||
return original; // Creates another box, does not reference boxed primitive
|
||||
} else if(originalType.IsArray) {
|
||||
return deepCloneArrayPropertyBased((Array)original, originalType.GetElementType());
|
||||
} else {
|
||||
return deepCloneComplexPropertyBased(original);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Clones a complex type using property-based value transfer</summary>
|
||||
/// <param name="original">Original instance that will be cloned</param>
|
||||
/// <returns>A clone of the original instance</returns>
|
||||
private static object deepCloneComplexPropertyBased(object original) {
|
||||
Type originalType = original.GetType();
|
||||
object clone = Activator.CreateInstance(originalType);
|
||||
|
||||
PropertyInfo[] propertyInfos = originalType.GetProperties(
|
||||
BindingFlags.Public | BindingFlags.NonPublic |
|
||||
BindingFlags.Instance | BindingFlags.FlattenHierarchy
|
||||
);
|
||||
for(int index = 0; index < propertyInfos.Length; ++index) {
|
||||
PropertyInfo propertyInfo = propertyInfos[index];
|
||||
if(propertyInfo.CanRead && propertyInfo.CanWrite) {
|
||||
Type propertyType = propertyInfo.PropertyType;
|
||||
object originalValue = propertyInfo.GetValue(original, null);
|
||||
if(originalValue != null) {
|
||||
if(propertyType.IsPrimitive || (propertyType == typeof(string))) {
|
||||
// Primitive types can be assigned directly
|
||||
propertyInfo.SetValue(clone, originalValue, null);
|
||||
} else if(propertyType.IsArray) { // Arrays need to be cloned element-by-element
|
||||
propertyInfo.SetValue(
|
||||
clone,
|
||||
deepCloneArrayPropertyBased((Array)originalValue, propertyType.GetElementType()),
|
||||
null
|
||||
);
|
||||
} else { // Complex types need to be cloned member-by-member
|
||||
propertyInfo.SetValue(clone, deepCloneSinglePropertyBased(originalValue), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>Clones an array using property-based value transfer</summary>
|
||||
/// <param name="original">Original array that will be cloned</param>
|
||||
/// <param name="elementType">Type of elements the original array contains</param>
|
||||
/// <returns>A clone of the original array</returns>
|
||||
private static object deepCloneArrayPropertyBased(Array original, Type elementType) {
|
||||
if(elementType.IsPrimitive || (elementType == typeof(string))) {
|
||||
return original.Clone();
|
||||
}
|
||||
|
||||
int dimensionCount = original.Rank;
|
||||
|
||||
// Find out the length of each of the array's dimensions, also calculate how
|
||||
// many elements there are in the array in total.
|
||||
var lengths = new int[dimensionCount];
|
||||
int totalElementCount = 0;
|
||||
for(int index = 0; index < dimensionCount; ++index) {
|
||||
lengths[index] = original.GetLength(index);
|
||||
if(index == 0) {
|
||||
totalElementCount = lengths[index];
|
||||
} else {
|
||||
totalElementCount *= lengths[index];
|
||||
}
|
||||
}
|
||||
|
||||
// Knowing the number of dimensions and the length of each dimension, we can
|
||||
// create another array of the exact same sizes.
|
||||
Array clone = Array.CreateInstance(elementType, lengths);
|
||||
|
||||
// If this is a one-dimensional array (most common type), do an optimized copy
|
||||
// directly specifying the indices
|
||||
if(dimensionCount == 1) {
|
||||
|
||||
// Clone each element of the array directly
|
||||
for(int index = 0; index < totalElementCount; ++index) {
|
||||
object originalElement = original.GetValue(index);
|
||||
if(originalElement != null) {
|
||||
clone.SetValue(deepCloneSinglePropertyBased(originalElement), index);
|
||||
}
|
||||
}
|
||||
|
||||
} else { // Otherwise use the generic code for multi-dimensional arrays
|
||||
|
||||
var indices = new int[dimensionCount];
|
||||
for(int index = 0; index < totalElementCount; ++index) {
|
||||
|
||||
// Determine the index for each of the array's dimensions
|
||||
int elementIndex = index;
|
||||
for(int dimensionIndex = dimensionCount - 1; dimensionIndex >= 0; --dimensionIndex) {
|
||||
indices[dimensionIndex] = elementIndex % lengths[dimensionIndex];
|
||||
elementIndex /= lengths[dimensionIndex];
|
||||
}
|
||||
|
||||
// Clone the current array element
|
||||
object originalElement = original.GetValue(indices);
|
||||
if(originalElement != null) {
|
||||
clone.SetValue(deepCloneSinglePropertyBased(originalElement), indices);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
|
|
@ -1,146 +1,145 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 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 {
|
||||
|
||||
/// <summary>Unit Test for the binary serializer-based cloner</summary>
|
||||
[TestFixture]
|
||||
internal class SerializationClonerTest : CloneFactoryTest {
|
||||
|
||||
/// <summary>Initializes a new unit test suite for the reflection cloner</summary>
|
||||
public SerializationClonerTest() {
|
||||
this.cloneFactory = new SerializationCloner();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that cloning a null object simply returns null</summary>
|
||||
[Test]
|
||||
public void CloningNullYieldsNull() {
|
||||
Assert.IsNull(this.cloneFactory.DeepFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.DeepPropertyClone<object>(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that clones of objects whose class doesn't possess a default constructor
|
||||
/// can be made
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ClassWithoutDefaultConstructorCanBeCloned() {
|
||||
var original = new ClassWithoutDefaultConstructor(1234);
|
||||
ClassWithoutDefaultConstructor clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.Dummy, clone.Dummy);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clones of primitive types can be created</summary>
|
||||
[Test]
|
||||
public void PrimitiveTypesCanBeCloned() {
|
||||
int original = 12345;
|
||||
int clone = this.cloneFactory.DeepFieldClone(original);
|
||||
Assert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void DeepClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original[0], clone[0]);
|
||||
Assert.AreEqual(original[0].TestField, clone[0].TestField);
|
||||
Assert.AreEqual(original[0].TestProperty, clone[0].TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic list can be made</summary>
|
||||
[Test]
|
||||
public void GenericListsCanBeCloned() {
|
||||
var original = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
List<int> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic dictionary can be made</summary>
|
||||
[Test]
|
||||
public void GenericDictionariesCanBeCloned() {
|
||||
var original = new Dictionary<int, string>();
|
||||
original.Add(1, "one");
|
||||
Dictionary<int, string> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreEqual("one", clone[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>Clone factory being tested</summary>
|
||||
private ICloneFactory cloneFactory;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
||||
#endif // UNITTEST
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Unit Test for the binary serializer-based cloner</summary>
|
||||
[TestFixture]
|
||||
internal class SerializationClonerTest : CloneFactoryTest {
|
||||
|
||||
/// <summary>Initializes a new unit test suite for the reflection cloner</summary>
|
||||
public SerializationClonerTest() {
|
||||
this.cloneFactory = new SerializationCloner();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that cloning a null object simply returns null</summary>
|
||||
[Test]
|
||||
public void CloningNullYieldsNull() {
|
||||
Assert.IsNull(this.cloneFactory.DeepFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.DeepPropertyClone<object>(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that clones of objects whose class doesn't possess a default constructor
|
||||
/// can be made
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ClassWithoutDefaultConstructorCanBeCloned() {
|
||||
var original = new ClassWithoutDefaultConstructor(1234);
|
||||
ClassWithoutDefaultConstructor clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.Dummy, clone.Dummy);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clones of primitive types can be created</summary>
|
||||
[Test]
|
||||
public void PrimitiveTypesCanBeCloned() {
|
||||
int original = 12345;
|
||||
int clone = this.cloneFactory.DeepFieldClone(original);
|
||||
Assert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void DeepClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original[0], clone[0]);
|
||||
Assert.AreEqual(original[0].TestField, clone[0].TestField);
|
||||
Assert.AreEqual(original[0].TestProperty, clone[0].TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic list can be made</summary>
|
||||
[Test]
|
||||
public void GenericListsCanBeCloned() {
|
||||
var original = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
List<int> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic dictionary can be made</summary>
|
||||
[Test]
|
||||
public void GenericDictionariesCanBeCloned() {
|
||||
var original = new Dictionary<int, string>();
|
||||
original.Add(1, "one");
|
||||
Dictionary<int, string> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreEqual("one", clone[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>Clone factory being tested</summary>
|
||||
private ICloneFactory cloneFactory;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
|
@ -1,328 +1,327 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 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 {
|
||||
|
||||
/// <summary>Clones objects via serialization</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// 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.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 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
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class SerializationCloner : ICloneFactory {
|
||||
|
||||
#region class StaticSurrogateSelector
|
||||
|
||||
/// <summary>Selects a static surrogate for any non-primitive types</summary>
|
||||
private class StaticSurrogateSelector : ISurrogateSelector {
|
||||
|
||||
/// <summary>Initializes a new static surrogate selector</summary>
|
||||
/// <param name="staticSurrogate">Surrogate that will be selected</param>
|
||||
public StaticSurrogateSelector(ISerializationSurrogate staticSurrogate) {
|
||||
this.staticSurrogate = staticSurrogate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the next selector to escalate to if this one can't provide a surrogate
|
||||
/// </summary>
|
||||
/// <param name="selector">Selector to escalate to</param>
|
||||
public void ChainSelector(ISurrogateSelector selector) {
|
||||
this.chainedSelector = selector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the selector this one will escalate to if it can't provide a surrogate
|
||||
/// </summary>
|
||||
/// <returns>The selector this one will escalate to</returns>
|
||||
public ISurrogateSelector GetNextSelector() {
|
||||
return this.chainedSelector;
|
||||
}
|
||||
|
||||
/// <summary>Attempts to provides a surrogate for the specified type</summary>
|
||||
/// <param name="type">Type a surrogate will be provided for</param>
|
||||
/// <param name="context">Context </param>
|
||||
/// <param name="selector"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Surrogate the that will be selected for any non-primitive types</summary>
|
||||
private readonly ISerializationSurrogate staticSurrogate;
|
||||
/// <summary>Surrogate selector to escalate to if no surrogate can be provided</summary>
|
||||
private ISurrogateSelector chainedSelector;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class StaticSurrogateSelector
|
||||
|
||||
#region class FieldSerializationSurrogate
|
||||
|
||||
/// <summary>Serializes a type based on its fields</summary>
|
||||
private class FieldSerializationSurrogate : ISerializationSurrogate {
|
||||
|
||||
/// <summary>Extracts the data to be serialized from an object</summary>
|
||||
/// <param name="objectToSerialize">Object that is being serialized</param>
|
||||
/// <param name="info">Stores the serialized informations</param>
|
||||
/// <param name="context">
|
||||
/// Provides additional informations about the serialization process
|
||||
/// </param>
|
||||
public void GetObjectData(
|
||||
object objectToSerialize,
|
||||
SerializationInfo info,
|
||||
StreamingContext context
|
||||
) {
|
||||
Type originalType = objectToSerialize.GetType();
|
||||
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
info.AddValue(fieldInfo.Name, fieldInfo.GetValue(objectToSerialize));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reinserts saved data into a deserializd object</summary>
|
||||
/// <param name="deserializedObject">Object the saved data will be inserted into</param>
|
||||
/// <param name="info">Contains the serialized informations</param>
|
||||
/// <param name="context">
|
||||
/// Provides additional informations about the serialization process
|
||||
/// </param>
|
||||
/// <param name="selector">Surrogate selector that specified this surrogate</param>
|
||||
/// <returns>The deserialized object</returns>
|
||||
public object SetObjectData(
|
||||
object deserializedObject,
|
||||
SerializationInfo info,
|
||||
StreamingContext context,
|
||||
ISurrogateSelector selector
|
||||
) {
|
||||
Type originalType = deserializedObject.GetType();
|
||||
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
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
|
||||
|
||||
/// <summary>Serializes a type based on its properties</summary>
|
||||
private class PropertySerializationSurrogate : ISerializationSurrogate {
|
||||
|
||||
/// <summary>Extracts the data to be serialized from an object</summary>
|
||||
/// <param name="objectToSerialize">Object that is being serialized</param>
|
||||
/// <param name="info">Stores the serialized informations</param>
|
||||
/// <param name="context">
|
||||
/// Provides additional informations about the serialization process
|
||||
/// </param>
|
||||
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];
|
||||
if(propertyInfo.CanRead && propertyInfo.CanWrite) {
|
||||
info.AddValue(propertyInfo.Name, propertyInfo.GetValue(objectToSerialize, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reinserts saved data into a deserializd object</summary>
|
||||
/// <param name="deserializedObject">Object the saved data will be inserted into</param>
|
||||
/// <param name="info">Contains the serialized informations</param>
|
||||
/// <param name="context">
|
||||
/// Provides additional informations about the serialization process
|
||||
/// </param>
|
||||
/// <param name="selector">Surrogate selector that specified this surrogate</param>
|
||||
/// <returns>The deserialized object</returns>
|
||||
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];
|
||||
if(propertyInfo.CanRead && propertyInfo.CanWrite) {
|
||||
propertyInfo.SetValue(
|
||||
deserializedObject,
|
||||
info.GetValue(propertyInfo.Name, propertyInfo.PropertyType),
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return deserializedObject;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion // class PropertySerializationSurrogate
|
||||
|
||||
/// <summary>Initializes the static members of the serialization-based cloner</summary>
|
||||
static SerializationCloner() {
|
||||
fieldBasedFormatter = new BinaryFormatter(
|
||||
new StaticSurrogateSelector(new FieldSerializationSurrogate()),
|
||||
new StreamingContext(StreamingContextStates.All)
|
||||
);
|
||||
propertyBasedFormatter = new BinaryFormatter(
|
||||
new StaticSurrogateSelector(new PropertySerializationSurrogate()),
|
||||
new StreamingContext(StreamingContextStates.All)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
if(typeof(TCloned).IsClass || typeof(TCloned).IsArray) {
|
||||
if(ReferenceEquals(objectToClone, null)) {
|
||||
return default(TCloned);
|
||||
}
|
||||
}
|
||||
using(var memoryStream = new MemoryStream()) {
|
||||
fieldBasedFormatter.Serialize(memoryStream, objectToClone);
|
||||
memoryStream.Position = 0;
|
||||
return (TCloned)fieldBasedFormatter.Deserialize(memoryStream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
if(typeof(TCloned).IsClass || typeof(TCloned).IsArray) {
|
||||
if(ReferenceEquals(objectToClone, null)) {
|
||||
return default(TCloned);
|
||||
}
|
||||
}
|
||||
using(var memoryStream = new MemoryStream()) {
|
||||
propertyBasedFormatter.Serialize(memoryStream, objectToClone);
|
||||
memoryStream.Position = 0;
|
||||
return (TCloned)propertyBasedFormatter.Deserialize(memoryStream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowFieldClone<TCloned>(TCloned objectToClone) {
|
||||
throw new NotSupportedException("The serialization cloner cannot create shallow clones");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
throw new NotSupportedException("The serialization cloner cannot create shallow clones");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
return SerializationCloner.DeepFieldClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
return SerializationCloner.DeepPropertyClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>Serializes objects by storing their fields</summary>
|
||||
private static readonly BinaryFormatter fieldBasedFormatter;
|
||||
/// <summary>Serializes objects by storing their properties</summary>
|
||||
private static readonly BinaryFormatter propertyBasedFormatter;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Clones objects via serialization</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// 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.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 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
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class SerializationCloner : ICloneFactory {
|
||||
|
||||
#region class StaticSurrogateSelector
|
||||
|
||||
/// <summary>Selects a static surrogate for any non-primitive types</summary>
|
||||
private class StaticSurrogateSelector : ISurrogateSelector {
|
||||
|
||||
/// <summary>Initializes a new static surrogate selector</summary>
|
||||
/// <param name="staticSurrogate">Surrogate that will be selected</param>
|
||||
public StaticSurrogateSelector(ISerializationSurrogate staticSurrogate) {
|
||||
this.staticSurrogate = staticSurrogate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the next selector to escalate to if this one can't provide a surrogate
|
||||
/// </summary>
|
||||
/// <param name="selector">Selector to escalate to</param>
|
||||
public void ChainSelector(ISurrogateSelector selector) {
|
||||
this.chainedSelector = selector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the selector this one will escalate to if it can't provide a surrogate
|
||||
/// </summary>
|
||||
/// <returns>The selector this one will escalate to</returns>
|
||||
public ISurrogateSelector GetNextSelector() {
|
||||
return this.chainedSelector;
|
||||
}
|
||||
|
||||
/// <summary>Attempts to provides a surrogate for the specified type</summary>
|
||||
/// <param name="type">Type a surrogate will be provided for</param>
|
||||
/// <param name="context">Context </param>
|
||||
/// <param name="selector"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Surrogate the that will be selected for any non-primitive types</summary>
|
||||
private readonly ISerializationSurrogate staticSurrogate;
|
||||
/// <summary>Surrogate selector to escalate to if no surrogate can be provided</summary>
|
||||
private ISurrogateSelector chainedSelector;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class StaticSurrogateSelector
|
||||
|
||||
#region class FieldSerializationSurrogate
|
||||
|
||||
/// <summary>Serializes a type based on its fields</summary>
|
||||
private class FieldSerializationSurrogate : ISerializationSurrogate {
|
||||
|
||||
/// <summary>Extracts the data to be serialized from an object</summary>
|
||||
/// <param name="objectToSerialize">Object that is being serialized</param>
|
||||
/// <param name="info">Stores the serialized informations</param>
|
||||
/// <param name="context">
|
||||
/// Provides additional informations about the serialization process
|
||||
/// </param>
|
||||
public void GetObjectData(
|
||||
object objectToSerialize,
|
||||
SerializationInfo info,
|
||||
StreamingContext context
|
||||
) {
|
||||
Type originalType = objectToSerialize.GetType();
|
||||
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
info.AddValue(fieldInfo.Name, fieldInfo.GetValue(objectToSerialize));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reinserts saved data into a deserializd object</summary>
|
||||
/// <param name="deserializedObject">Object the saved data will be inserted into</param>
|
||||
/// <param name="info">Contains the serialized informations</param>
|
||||
/// <param name="context">
|
||||
/// Provides additional informations about the serialization process
|
||||
/// </param>
|
||||
/// <param name="selector">Surrogate selector that specified this surrogate</param>
|
||||
/// <returns>The deserialized object</returns>
|
||||
public object SetObjectData(
|
||||
object deserializedObject,
|
||||
SerializationInfo info,
|
||||
StreamingContext context,
|
||||
ISurrogateSelector selector
|
||||
) {
|
||||
Type originalType = deserializedObject.GetType();
|
||||
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
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
|
||||
|
||||
/// <summary>Serializes a type based on its properties</summary>
|
||||
private class PropertySerializationSurrogate : ISerializationSurrogate {
|
||||
|
||||
/// <summary>Extracts the data to be serialized from an object</summary>
|
||||
/// <param name="objectToSerialize">Object that is being serialized</param>
|
||||
/// <param name="info">Stores the serialized informations</param>
|
||||
/// <param name="context">
|
||||
/// Provides additional informations about the serialization process
|
||||
/// </param>
|
||||
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];
|
||||
if(propertyInfo.CanRead && propertyInfo.CanWrite) {
|
||||
info.AddValue(propertyInfo.Name, propertyInfo.GetValue(objectToSerialize, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reinserts saved data into a deserializd object</summary>
|
||||
/// <param name="deserializedObject">Object the saved data will be inserted into</param>
|
||||
/// <param name="info">Contains the serialized informations</param>
|
||||
/// <param name="context">
|
||||
/// Provides additional informations about the serialization process
|
||||
/// </param>
|
||||
/// <param name="selector">Surrogate selector that specified this surrogate</param>
|
||||
/// <returns>The deserialized object</returns>
|
||||
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];
|
||||
if(propertyInfo.CanRead && propertyInfo.CanWrite) {
|
||||
propertyInfo.SetValue(
|
||||
deserializedObject,
|
||||
info.GetValue(propertyInfo.Name, propertyInfo.PropertyType),
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return deserializedObject;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion // class PropertySerializationSurrogate
|
||||
|
||||
/// <summary>Initializes the static members of the serialization-based cloner</summary>
|
||||
static SerializationCloner() {
|
||||
fieldBasedFormatter = new BinaryFormatter(
|
||||
new StaticSurrogateSelector(new FieldSerializationSurrogate()),
|
||||
new StreamingContext(StreamingContextStates.All)
|
||||
);
|
||||
propertyBasedFormatter = new BinaryFormatter(
|
||||
new StaticSurrogateSelector(new PropertySerializationSurrogate()),
|
||||
new StreamingContext(StreamingContextStates.All)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
if(typeof(TCloned).IsClass || typeof(TCloned).IsArray) {
|
||||
if(ReferenceEquals(objectToClone, null)) {
|
||||
return default(TCloned);
|
||||
}
|
||||
}
|
||||
using(var memoryStream = new MemoryStream()) {
|
||||
fieldBasedFormatter.Serialize(memoryStream, objectToClone);
|
||||
memoryStream.Position = 0;
|
||||
return (TCloned)fieldBasedFormatter.Deserialize(memoryStream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
public static TCloned DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
if(typeof(TCloned).IsClass || typeof(TCloned).IsArray) {
|
||||
if(ReferenceEquals(objectToClone, null)) {
|
||||
return default(TCloned);
|
||||
}
|
||||
}
|
||||
using(var memoryStream = new MemoryStream()) {
|
||||
propertyBasedFormatter.Serialize(memoryStream, objectToClone);
|
||||
memoryStream.Position = 0;
|
||||
return (TCloned)propertyBasedFormatter.Deserialize(memoryStream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowFieldClone<TCloned>(TCloned objectToClone) {
|
||||
throw new NotSupportedException("The serialization cloner cannot create shallow clones");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow clone of the specified object, reusing any referenced objects
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A shallow clone of the provided object</returns>
|
||||
TCloned ICloneFactory.ShallowPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
throw new NotSupportedException("The serialization cloner cannot create shallow clones");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepFieldClone<TCloned>(TCloned objectToClone) {
|
||||
return SerializationCloner.DeepFieldClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the specified object, also creating clones of all
|
||||
/// child objects being referenced
|
||||
/// </summary>
|
||||
/// <typeparam name="TCloned">Type of the object that will be cloned</typeparam>
|
||||
/// <param name="objectToClone">Object that will be cloned</param>
|
||||
/// <returns>A deep clone of the provided object</returns>
|
||||
TCloned ICloneFactory.DeepPropertyClone<TCloned>(TCloned objectToClone) {
|
||||
return SerializationCloner.DeepPropertyClone<TCloned>(objectToClone);
|
||||
}
|
||||
|
||||
/// <summary>Serializes objects by storing their fields</summary>
|
||||
private static readonly BinaryFormatter fieldBasedFormatter;
|
||||
/// <summary>Serializes objects by storing their properties</summary>
|
||||
private static readonly BinaryFormatter propertyBasedFormatter;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue