Fixed a compilation error when targeting Windows Phone; ChainStream now uses a params list for its constructor; added object cloning framework with support for deep and shallow cloning as well as cloning based on fields or properties

git-svn-id: file:///srv/devel/repo-conversion/nusu@223 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2012-02-01 22:45:15 +00:00
parent 54d82e9659
commit eb3083cf9e
12 changed files with 1290 additions and 5 deletions

View File

@ -60,6 +60,14 @@
<Compile Include="Source\AffineThreadPool.Test.cs"> <Compile Include="Source\AffineThreadPool.Test.cs">
<DependentUpon>AffineThreadPool.cs</DependentUpon> <DependentUpon>AffineThreadPool.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\Cloning\CloningParameters.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.cs" />
<Compile Include="Source\Cloning\IStateCopier.cs" />
<Compile Include="Source\Cloning\ReflectionCloner.cs" />
<Compile Include="Source\Cloning\ReflectionCloner.Test.cs">
<DependentUpon>ReflectionCloner.cs</DependentUpon>
</Compile>
<Compile Include="Source\Cloning\ICloneFactory.cs" />
<Compile Include="Source\Collections\Deque.cs" /> <Compile Include="Source\Collections\Deque.cs" />
<Compile Include="Source\Collections\Deque.Insertion.cs"> <Compile Include="Source\Collections\Deque.Insertion.cs">
<DependentUpon>Deque.cs</DependentUpon> <DependentUpon>Deque.cs</DependentUpon>
@ -280,7 +288,11 @@
<Link>Foundation.snk</Link> <Link>Foundation.snk</Link>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup>
<Content Include="Documents\CommandLine.txt" />
<Content Include="Documents\Nuclex.Support.txt" />
<Content Include="Documents\Request Framework.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -91,6 +91,14 @@
<Compile Include="Source\AffineThreadPool.Test.cs"> <Compile Include="Source\AffineThreadPool.Test.cs">
<DependentUpon>AffineThreadPool.cs</DependentUpon> <DependentUpon>AffineThreadPool.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\Cloning\CloningParameters.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.cs" />
<Compile Include="Source\Cloning\ICloneFactory.cs" />
<Compile Include="Source\Cloning\IStateCopier.cs" />
<Compile Include="Source\Cloning\ReflectionCloner.cs" />
<Compile Include="Source\Cloning\ReflectionCloner.Test.cs">
<DependentUpon>ReflectionCloner.cs</DependentUpon>
</Compile>
<Compile Include="Source\Collections\Deque.cs" /> <Compile Include="Source\Collections\Deque.cs" />
<Compile Include="Source\Collections\Deque.Insertion.cs"> <Compile Include="Source\Collections\Deque.Insertion.cs">
<DependentUpon>Deque.cs</DependentUpon> <DependentUpon>Deque.cs</DependentUpon>
@ -343,6 +351,11 @@
<Link>Foundation.snk</Link> <Link>Foundation.snk</Link>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Documents\CommandLine.txt" />
<Content Include="Documents\Nuclex.Support.txt" />
<Content Include="Documents\Request Framework.txt" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" /> <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" />
<!-- <!--

View File

@ -102,6 +102,14 @@
<Compile Include="Source\AffineThreadPool.Test.cs"> <Compile Include="Source\AffineThreadPool.Test.cs">
<DependentUpon>AffineThreadPool.cs</DependentUpon> <DependentUpon>AffineThreadPool.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\Cloning\CloningParameters.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.cs" />
<Compile Include="Source\Cloning\ICloneFactory.cs" />
<Compile Include="Source\Cloning\IStateCopier.cs" />
<Compile Include="Source\Cloning\ReflectionCloner.cs" />
<Compile Include="Source\Cloning\ReflectionCloner.Test.cs">
<DependentUpon>ReflectionCloner.cs</DependentUpon>
</Compile>
<Compile Include="Source\Collections\Deque.cs" /> <Compile Include="Source\Collections\Deque.cs" />
<Compile Include="Source\Collections\Deque.Insertion.cs"> <Compile Include="Source\Collections\Deque.Insertion.cs">
<DependentUpon>Deque.cs</DependentUpon> <DependentUpon>Deque.cs</DependentUpon>

View File

@ -88,7 +88,7 @@ namespace Nuclex.Support {
// as we may run into situations where multiple operations need to be atomic. // as we may run into situations where multiple operations need to be atomic.
// We keep track of the threads we've created just for good measure; not actually // We keep track of the threads we've created just for good measure; not actually
// needed for any core functionality. // needed for any core functionality.
#if XBOX360 #if XBOX360 || WINDOWS_PHONE
workAvailable = new Semaphore(); workAvailable = new Semaphore();
#else #else
workAvailable = new System.Threading.Semaphore(0, Processors); workAvailable = new System.Threading.Semaphore(0, Processors);
@ -303,7 +303,7 @@ namespace Nuclex.Support {
/// <summary> /// <summary>
/// Used to let the threads in the thread pool wait for new work to appear. /// Used to let the threads in the thread pool wait for new work to appear.
/// </summary> /// </summary>
#if XBOX360 #if XBOX360 || WINDOWS_PHONE
private static Semaphore workAvailable; private static Semaphore workAvailable;
#else #else
private static System.Threading.Semaphore workAvailable; private static System.Threading.Semaphore workAvailable;

View File

@ -0,0 +1,65 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2010 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
namespace Nuclex.Support.Cloning {
/// <summary>Parameters of a cloning operation</summary>
public struct CloningParameters {
/// <summary>Initializes new cloning parameters</summary>
/// <param name="type">Type that will be cloned</param>
/// <param name="deep">Whether to perform a deep clone</param>
public CloningParameters(Type type, bool deep) {
this.Type = type;
this.Deep = deep;
}
/// <summary>Returns a string description of the instance</summary>
/// <returns>The instance's string description</returns>
public override string ToString() {
if(this.Deep) {
return "Deep clone of " + this.Type.ToString();
} else {
return "Shallow clone of " + this.Type.ToString();
}
}
/// <summary>
/// Returns a hash code that is identical for instances with identical state
/// </summary>
/// <returns>The instance's hash code</returns>
public override int GetHashCode() {
if(this.Deep) {
return this.Type.GetHashCode();
} else {
return ~this.Type.GetHashCode();
}
}
/// <summary>Type that will be cloned</summary>
public Type Type;
/// <summary>Whether a deep clone will be performed</summary>
public bool Deep;
}
} // namespace Nuclex.Support.Cloning

View File

@ -0,0 +1,153 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2010 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Nuclex.Support.Cloning {
#if false
/// <summary>An action that takes its arguments as references to a structure</summary>
/// <typeparam name="TFirst">Type of the first argument to the method</typeparam>
/// <typeparam name="TSecond">Type of the second argument to the method</typeparam>
/// <param name="first">First argument to the method</param>
/// <param name="second">Second argument to the method</param>
public delegate void ReferenceAction<TFirst, TSecond>(ref TFirst first, ref TSecond second)
where TFirst : struct
where TSecond : struct;
/// <summary>
/// Cloning factory which uses expression trees to improve performance when cloning
/// is a high-frequency action.
/// </summary>
public class ExpressionTreeCloneFactory : ICloneFactory {
/// <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>
/// <param name="usePropertyBasedClone">
/// Whether to clone the object based on its properties only
/// </param>
/// <returns>A deep clone of the provided object</returns>
public TCloned DeepClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone)
where TCloned : new() {
throw new NotImplementedException();
}
/// <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>
/// <param name="usePropertyBasedClone">
/// Whether to clone the object based on its properties only
/// </param>
/// <returns>A shallow clone of the provided object</returns>
public TCloned ShallowClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone)
where TCloned : new() {
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>(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();
}
/// <summary>
/// Compiles a method that copies the state of one object into another object
/// </summary>
/// <typeparam name="TCloned">Type of object whose state will be copied</typeparam>
/// <param name="deepClone">Whether to create clones of the referenced objects</param>
/// <returns>A method that copies the state from one object into another object</returns>
public static Action<TCloned, TCloned> CreateReferenceCopier<TCloned>(bool deepClone)
where TCloned : class {
throw new NotImplementedException();
}
/// <summary>
/// Compiles a method that copies the state of one object into another object
/// </summary>
/// <typeparam name="TCloned">Type of object whose state will be copied</typeparam>
/// <param name="deepClone">Whether to create clones of the referenced objects</param>
/// <returns>A method that copies the state from one object into another object</returns>
public static ReferenceAction<TCloned, TCloned> CreateValueCopier<TCloned>(bool deepClone)
where TCloned : struct {
throw new NotImplementedException();
}
/// <summary>Compiles a method that creates a clone of an object</summary>
/// <typeparam name="TCloned">Type of object that will be cloned</typeparam>
/// <param name="deepClone">Whether to create clones of the referenced objects</param>
/// <returns>A method that clones an object of the provided type</returns>
public static Func<TCloned, TCloned> CreateCloner<TCloned>(bool deepClone)
where TCloned : class, new() {
throw new NotImplementedException();
}
}
#endif
} // namespace Nuclex.Support.Cloning

View File

@ -0,0 +1,65 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2010 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
namespace Nuclex.Support.Cloning {
/// <summary>Constructs new objects by cloning existing objects</summary>
public interface ICloneFactory {
/// <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>
/// <param name="usePropertyBasedClone">
/// Whether to clone the object based on its properties only
/// </param>
/// <returns>A deep clone of the provided object</returns>
/// <remarks>
/// 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.
/// </remarks>
TCloned DeepClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone);
/// <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>
/// <param name="usePropertyBasedClone">
/// Whether to clone the object based on its properties only
/// </param>
/// <returns>A shallow clone of the provided object</returns>
/// <remarks>
/// 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.
/// </remarks>
TCloned ShallowClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone);
}
} // namespace Nuclex.Support.Cloning

View File

@ -0,0 +1,90 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2010 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
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

View File

@ -0,0 +1,450 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2010 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.IO;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Cloning {
/// <summary>Unit Test for the reflection-based cloner</summary>
[TestFixture]
public class ReflectionClonerTest {
#region class TestReferenceType
/// <summary>A reference type being used for testing</summary>
private class TestReferenceType {
/// <summary>Field holding an integer value for testing</summary>
public int TestField;
/// <summary>Property holding an integer value for testing</summary>
public int TestProperty { get; set; }
}
#endregion // class TestReferenceType
#region struct TestValueType
/// <summary>A value type being used for testing</summary>
private struct TestValueType {
/// <summary>Field holding an integer value for testing</summary>
public int TestField;
/// <summary>Property holding an integer value for testing</summary>
public int TestProperty { get; set; }
}
#endregion // struct TestValueType
#region struct HierarchicalValueType
/// <summary>A value type containiner other complex types used for testing</summary>
private struct HierarchicalValueType {
/// <summary>Field holding an integer value for testing</summary>
public int TestField;
/// <summary>Property holding an integer value for testing</summary>
public int TestProperty { get; set; }
/// <summary>Value type field for testing</summary>
public TestValueType ValueTypeField;
/// <summary>Value type property for testing</summary>
public TestValueType ValueTypeProperty { get; set; }
/// <summary>Reference type field for testing</summary>
public TestReferenceType ReferenceTypeField;
/// <summary>Reference type property for testing</summary>
public TestReferenceType ReferenceTypeProperty { get; set; }
}
#endregion // struct HierarchicalValueType
#region struct HierarchicalReferenceType
/// <summary>A value type containiner other complex types used for testing</summary>
private class HierarchicalReferenceType {
/// <summary>Field holding an integer value for testing</summary>
public int TestField;
/// <summary>Property holding an integer value for testing</summary>
public int TestProperty { get; set; }
/// <summary>Value type field for testing</summary>
public TestValueType ValueTypeField;
/// <summary>Value type property for testing</summary>
public TestValueType ValueTypeProperty { get; set; }
/// <summary>Reference type field for testing</summary>
public TestReferenceType ReferenceTypeField;
/// <summary>Reference type property for testing</summary>
public TestReferenceType ReferenceTypeProperty { get; set; }
}
#endregion // struct HierarchicalReferenceType
/// <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 = (new ReflectionCloner()).ShallowClone(original, false);
verifyShallowFieldBasedValueTypeCopy(ref original, ref clone);
}
/// <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 = (new ReflectionCloner()).ShallowClone(original, false);
verifyShallowFieldBasedReferenceTypeCopy(original, clone);
}
/// <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 = (new ReflectionCloner()).DeepClone(original, false);
verifyDeepFieldBasedValueTypeCopy(ref original, ref clone);
}
/// <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 = (new ReflectionCloner()).DeepClone(original, false);
verifyDeepFieldBasedReferenceTypeCopy(ref original, ref clone);
}
/// <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 = (new ReflectionCloner()).ShallowClone(original, true);
verifyShallowPropertyBasedValueTypeCopy(ref original, ref clone);
}
/// <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 = (new ReflectionCloner()).ShallowClone(original, true);
verifyShallowPropertyBasedReferenceTypeCopy(ref original, ref clone);
}
/// <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 = (new ReflectionCloner()).DeepClone(original, true);
verifyDeepPropertyBasedValueTypeCopy(ref original, ref clone);
}
/// <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 = (new ReflectionCloner()).DeepClone(original, true);
verifyDeepPropertyBasedReferenceTypeCopy(ref original, ref clone);
}
/// <summary>
/// Verifies that a field-based shallow clone of a value type matches
/// the expected outcome for this type of clone
/// </summary>
/// <param name="original">Original instance that has been cloned</param>
/// <param name="clone">Cloned instance that will be verified</param>
private static void verifyShallowFieldBasedValueTypeCopy(
ref HierarchicalValueType original, ref HierarchicalValueType clone
) {
Assert.AreEqual(original.TestField, clone.TestField);
Assert.AreEqual(original.TestProperty, clone.TestProperty);
Assert.AreEqual(original.ValueTypeField.TestField, clone.ValueTypeField.TestField);
Assert.AreEqual(original.ValueTypeField.TestProperty, clone.ValueTypeField.TestProperty);
Assert.AreEqual(
original.ValueTypeProperty.TestField, clone.ValueTypeProperty.TestField
);
Assert.AreEqual(
original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
);
Assert.AreSame(original.ReferenceTypeField, clone.ReferenceTypeField);
Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
}
/// <summary>
/// Verifies that a field-based shallow clone of a reference type matches
/// the expected outcome for this type of clone
/// </summary>
/// <param name="original">Original instance that has been cloned</param>
/// <param name="clone">Cloned instance that will be verified</param>
private static void verifyShallowFieldBasedReferenceTypeCopy(
HierarchicalReferenceType original, HierarchicalReferenceType clone
) {
Assert.AreEqual(original.TestField, clone.TestField);
Assert.AreEqual(original.TestProperty, clone.TestProperty);
Assert.AreEqual(original.ValueTypeField.TestField, clone.ValueTypeField.TestField);
Assert.AreEqual(original.ValueTypeField.TestProperty, clone.ValueTypeField.TestProperty);
Assert.AreEqual(
original.ValueTypeProperty.TestField, clone.ValueTypeProperty.TestField
);
Assert.AreEqual(
original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
);
Assert.AreSame(original.ReferenceTypeField, clone.ReferenceTypeField);
Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
}
/// <summary>
/// Verifies that a field-based deep clone of a value type matches
/// the expected outcome for this type of clone
/// </summary>
/// <param name="original">Original instance that has been cloned</param>
/// <param name="clone">Cloned instance that will be verified</param>
private static void verifyDeepFieldBasedValueTypeCopy(
ref HierarchicalValueType original, ref HierarchicalValueType clone
) {
Assert.AreEqual(original.TestField, clone.TestField);
Assert.AreEqual(original.TestProperty, clone.TestProperty);
Assert.AreEqual(original.ValueTypeField.TestField, clone.ValueTypeField.TestField);
Assert.AreEqual(original.ValueTypeField.TestProperty, clone.ValueTypeField.TestProperty);
Assert.AreEqual(
original.ValueTypeProperty.TestField, clone.ValueTypeProperty.TestField
);
Assert.AreEqual(
original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
);
Assert.AreEqual(
original.ReferenceTypeField.TestField, clone.ReferenceTypeField.TestField
);
Assert.AreEqual(
original.ReferenceTypeField.TestProperty, clone.ReferenceTypeField.TestProperty
);
Assert.AreEqual(
original.ReferenceTypeProperty.TestField, clone.ReferenceTypeProperty.TestField
);
Assert.AreEqual(
original.ReferenceTypeProperty.TestProperty, clone.ReferenceTypeProperty.TestProperty
);
Assert.AreNotSame(original.ReferenceTypeField, clone.ReferenceTypeField);
Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
}
/// <summary>
/// Verifies that a field-based deep clone of a reference type matches
/// the expected outcome for this type of clone
/// </summary>
/// <param name="original">Original instance that has been cloned</param>
/// <param name="clone">Cloned instance that will be verified</param>
private static void verifyDeepFieldBasedReferenceTypeCopy(
ref HierarchicalReferenceType original, ref HierarchicalReferenceType clone
) {
Assert.AreEqual(original.TestField, clone.TestField);
Assert.AreEqual(original.TestProperty, clone.TestProperty);
Assert.AreEqual(original.ValueTypeField.TestField, clone.ValueTypeField.TestField);
Assert.AreEqual(original.ValueTypeField.TestProperty, clone.ValueTypeField.TestProperty);
Assert.AreEqual(
original.ValueTypeProperty.TestField, clone.ValueTypeProperty.TestField
);
Assert.AreEqual(
original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
);
Assert.AreEqual(
original.ReferenceTypeField.TestField, clone.ReferenceTypeField.TestField
);
Assert.AreEqual(
original.ReferenceTypeField.TestProperty, clone.ReferenceTypeField.TestProperty
);
Assert.AreEqual(
original.ReferenceTypeProperty.TestField, clone.ReferenceTypeProperty.TestField
);
Assert.AreEqual(
original.ReferenceTypeProperty.TestProperty, clone.ReferenceTypeProperty.TestProperty
);
Assert.AreNotSame(original.ReferenceTypeField, clone.ReferenceTypeField);
Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
}
/// <summary>
/// Verifies that a property-based shallow clone of a value type matches
/// the expected outcome for this type of clone
/// </summary>
/// <param name="original">Original instance that has been cloned</param>
/// <param name="clone">Cloned instance that will be verified</param>
private static void verifyShallowPropertyBasedValueTypeCopy(
ref HierarchicalValueType original, ref HierarchicalValueType clone
) {
Assert.AreEqual(0, clone.TestField);
Assert.AreEqual(original.TestProperty, clone.TestProperty);
Assert.AreEqual(0, clone.ValueTypeField.TestField);
Assert.AreEqual(0, clone.ValueTypeField.TestProperty);
Assert.AreEqual(0, clone.ValueTypeProperty.TestField);
Assert.AreEqual(
original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
);
Assert.IsNull(clone.ReferenceTypeField);
Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
}
/// <summary>
/// Verifies that a property-based shallow clone of a reference type matches
/// the expected outcome for this type of clone
/// </summary>
/// <param name="original">Original instance that has been cloned</param>
/// <param name="clone">Cloned instance that will be verified</param>
private static void verifyShallowPropertyBasedReferenceTypeCopy(
ref HierarchicalReferenceType original, ref HierarchicalReferenceType clone
) {
Assert.AreEqual(0, clone.TestField);
Assert.AreEqual(original.TestProperty, clone.TestProperty);
Assert.AreEqual(0, clone.ValueTypeField.TestField);
Assert.AreEqual(0, clone.ValueTypeField.TestProperty);
Assert.AreEqual(0, clone.ValueTypeProperty.TestField);
Assert.AreEqual(
original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
);
Assert.IsNull(clone.ReferenceTypeField);
Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
}
/// <summary>
/// Verifies that a property-based deep clone of a value type matches
/// the expected outcome for this type of clone
/// </summary>
/// <param name="original">Original instance that has been cloned</param>
/// <param name="clone">Cloned instance that will be verified</param>
private static void verifyDeepPropertyBasedValueTypeCopy(
ref HierarchicalValueType original, ref HierarchicalValueType clone
) {
Assert.AreEqual(0, clone.TestField);
Assert.AreEqual(original.TestProperty, clone.TestProperty);
Assert.AreEqual(0, clone.ValueTypeField.TestField);
Assert.AreEqual(0, clone.ValueTypeField.TestProperty);
Assert.AreEqual(0, clone.ValueTypeProperty.TestField);
Assert.AreEqual(
original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
);
Assert.IsNull(clone.ReferenceTypeField);
Assert.AreEqual(0, clone.ReferenceTypeProperty.TestField);
Assert.AreEqual(
original.ReferenceTypeProperty.TestProperty, clone.ReferenceTypeProperty.TestProperty
);
Assert.IsNull(clone.ReferenceTypeField);
Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
}
/// <summary>
/// Verifies that a property-based deep clone of a reference type matches
/// the expected outcome for this type of clone
/// </summary>
/// <param name="original">Original instance that has been cloned</param>
/// <param name="clone">Cloned instance that will be verified</param>
private static void verifyDeepPropertyBasedReferenceTypeCopy(
ref HierarchicalReferenceType original, ref HierarchicalReferenceType clone
) {
Assert.AreEqual(0, clone.TestField);
Assert.AreEqual(original.TestProperty, clone.TestProperty);
Assert.AreEqual(0, clone.ValueTypeField.TestField);
Assert.AreEqual(0, clone.ValueTypeField.TestProperty);
Assert.AreEqual(0, clone.ValueTypeProperty.TestField);
Assert.AreEqual(
original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
);
Assert.IsNull(clone.ReferenceTypeField);
Assert.AreEqual(0, clone.ReferenceTypeProperty.TestField);
Assert.AreEqual(
original.ReferenceTypeProperty.TestProperty, clone.ReferenceTypeProperty.TestProperty
);
Assert.IsNull(clone.ReferenceTypeField);
Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
}
/// <summary>Creates a value type with random data for testing</summary>
/// <returns>A new value type with random data</returns>
private static HierarchicalValueType createValueType() {
return new HierarchicalValueType() {
TestField = 123,
TestProperty = 321,
ValueTypeField = new TestValueType() {
TestField = 456,
TestProperty = 654
},
ValueTypeProperty = new TestValueType() {
TestField = 789,
TestProperty = 987,
},
ReferenceTypeField = new TestReferenceType() {
TestField = 135,
TestProperty = 531
},
ReferenceTypeProperty = new TestReferenceType() {
TestField = 246,
TestProperty = 642,
}
};
}
/// <summary>Creates a reference type with random data for testing</summary>
/// <returns>A new reference type with random data</returns>
private static HierarchicalReferenceType createReferenceType() {
return new HierarchicalReferenceType() {
TestField = 123,
TestProperty = 321,
ValueTypeField = new TestValueType() {
TestField = 456,
TestProperty = 654
},
ValueTypeProperty = new TestValueType() {
TestField = 789,
TestProperty = 987,
},
ReferenceTypeField = new TestReferenceType() {
TestField = 135,
TestProperty = 531
},
ReferenceTypeProperty = new TestReferenceType() {
TestField = 246,
TestProperty = 642,
}
};
}
}
} // namespace Nuclex.Support.Cloning
#endif // UNITTEST

View File

@ -0,0 +1,429 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2010 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Reflection;
namespace Nuclex.Support.Cloning {
// TODO: Doesn't clone arrays yet
/// <summary>Clones objects using reflection</summary>
public class ReflectionCloner : ICloneFactory, IStateCopier {
/// <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>
/// <param name="usePropertyBasedClone">
/// Whether to clone the object based on its properties only
/// </param>
/// <returns>A shallow clone of the provided object</returns>
public TCloned ShallowClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone) {
if(typeof(TCloned).IsValueType) {
TCloned clone = Activator.CreateInstance<TCloned>();
if(usePropertyBasedClone) {
shallowCopyValueTypePropertyBased<TCloned>(ref objectToClone, ref clone);
} else {
shallowCopyValueTypeFieldBased<TCloned>(ref objectToClone, ref clone);
}
return clone;
} else {
TCloned clone = (TCloned)Activator.CreateInstance(objectToClone.GetType());
if(usePropertyBasedClone) {
shallowCopyReferenceTypePropertyBased(objectToClone.GetType(), objectToClone, clone);
} else {
shallowCopyReferenceTypeFieldBased(objectToClone.GetType(), objectToClone, clone);
}
return clone;
}
}
/// <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>
/// <param name="usePropertyBasedClone">
/// Whether to clone the object based on its properties only
/// </param>
/// <returns>A deep clone of the provided object</returns>
public TCloned DeepClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone) {
if(typeof(TCloned).IsValueType) {
TCloned clone = Activator.CreateInstance<TCloned>();
if(usePropertyBasedClone) {
deepCopyValueTypePropertyBased<TCloned>(ref objectToClone, ref clone);
} else {
deepCopyValueTypeFieldBased<TCloned>(ref objectToClone, ref clone);
}
return clone;
} else {
TCloned clone = (TCloned)Activator.CreateInstance(objectToClone.GetType());
if(usePropertyBasedClone) {
deepCopyReferenceTypePropertyBased(objectToClone.GetType(), objectToClone, clone);
} else {
deepCopyReferenceTypeFieldBased(objectToClone.GetType(), objectToClone, clone);
}
return clone;
}
}
/// <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 {
if(propertyBased) {
shallowCopyReferenceTypePropertyBased(typeof(TState), original, target);
} else {
shallowCopyReferenceTypeFieldBased(typeof(TState), original, target);
}
}
/// <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 {
if(propertyBased) {
shallowCopyValueTypePropertyBased<TState>(ref original, ref target);
} else {
shallowCopyValueTypeFieldBased<TState>(ref original, ref target);
}
}
/// <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 {
if(propertyBased) {
deepCopyReferenceTypePropertyBased(typeof(TState), original, target);
} else {
deepCopyReferenceTypeFieldBased(typeof(TState), original, target);
}
}
/// <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 {
if(propertyBased) {
deepCopyValueTypePropertyBased<TState>(ref original, ref target);
} else {
deepCopyValueTypeFieldBased<TState>(ref original, ref target);
}
}
/// <summary>Creates a field-based shallow copy of a reference type</summary>
/// <param name="copiedType">Type the copy will be based upon</param>
/// <param name="original">Original object that will be copied</param>
/// <param name="target">Target object into which copied values will be written</param>
private static void shallowCopyReferenceTypeFieldBased(
Type copiedType, object original, object target
) {
FieldInfo[] fieldInfos = copiedType.GetFields(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy
);
for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index];
fieldInfo.SetValue(target, fieldInfo.GetValue(original));
}
}
/// <summary>Creates a property-based shallow copy of a reference type</summary>
/// <param name="copiedType">Type the copy will be based upon</param>
/// <param name="original">Original object that will be copied</param>
/// <param name="target">Target object into which copied values will be written</param>
private static void shallowCopyReferenceTypePropertyBased(
Type copiedType, object original, object target
) {
PropertyInfo[] propertyInfos = copiedType.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) {
if(propertyInfo.PropertyType.IsPrimitive) {
propertyInfo.SetValue(
target,
propertyInfo.GetValue(original, null),
null
);
} else if(propertyInfo.PropertyType.IsValueType) {
// Recurse into the value type - value types are seen as part of
// the outer type and a shallow copy does follow their hierarchy.
// This is equivalent to what would happen if you directly assign
// one value type to another.
object boxedOriginalValue = propertyInfo.GetValue(original, null);
Type originalType = boxedOriginalValue.GetType();
object boxedClonedValue = Activator.CreateInstance(originalType);
shallowCopyReferenceTypePropertyBased(
originalType, boxedOriginalValue, boxedClonedValue
);
propertyInfo.SetValue(target, boxedClonedValue, null);
} else {
propertyInfo.SetValue(
target,
propertyInfo.GetValue(original, null),
null
);
}
}
}
}
/// <summary>Creates a property-based shallow copy of a value type</summary>
/// <typeparam name="TState">Value type that will be copied</typeparam>
/// <param name="original">Original object that will be copied</param>
/// <param name="target">Target object into which copied values will be written</param>
private static void shallowCopyValueTypePropertyBased<TState>(
ref TState original, ref TState target
) {
object boxedOriginal = original;
object boxedTarget = target;
PropertyInfo[] propertyInfos = typeof(TState).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) {
if(propertyInfo.PropertyType.IsPrimitive) {
propertyInfo.SetValue(
boxedTarget,
propertyInfo.GetValue(boxedOriginal, null),
null
);
} else if(propertyInfo.PropertyType.IsValueType) {
object boxedOriginalValue = propertyInfo.GetValue(original, null);
Type originalType = boxedOriginalValue.GetType();
object boxedClonedValue = Activator.CreateInstance(originalType);
deepCopyReferenceTypePropertyBased(
originalType, boxedOriginalValue, boxedClonedValue
);
propertyInfo.SetValue(boxedTarget, boxedClonedValue, null);
} else {
propertyInfo.SetValue(
boxedTarget,
propertyInfo.GetValue(boxedOriginal, null),
null
);
}
}
}
target = (TState)boxedTarget;
}
/// <summary>Creates a field-based shallow copy of a value type</summary>
/// <typeparam name="TState">Value type that will be copied</typeparam>
/// <param name="original">Original object that will be copied</param>
/// <param name="target">Target object into which copied values will be written</param>
private static void shallowCopyValueTypeFieldBased<TState>(
ref TState original, ref TState target
) {
target = original; // hehe
}
/// <summary>Creates a field-based deep copy of a reference type</summary>
/// <param name="copiedType">Type the copy will be based upon</param>
/// <param name="original">Original object that will be copied</param>
/// <param name="target">Target object into which copied values will be written</param>
private static void deepCopyReferenceTypeFieldBased(
Type copiedType, object original, object target
) {
FieldInfo[] fieldInfos = copiedType.GetFields(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy
);
for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index];
if(fieldInfo.FieldType.IsPrimitive) {
fieldInfo.SetValue(target, fieldInfo.GetValue(original));
} else if(fieldInfo.FieldType.IsValueType) {
object boxedOriginalValue = fieldInfo.GetValue(original);
Type originalType = boxedOriginalValue.GetType();
object boxedClonedValue = Activator.CreateInstance(originalType);
deepCopyReferenceTypeFieldBased(originalType, boxedOriginalValue, boxedClonedValue);
fieldInfo.SetValue(target, boxedClonedValue);
} else {
object originalValue = fieldInfo.GetValue(original);
if(originalValue != null) {
Type originalType = originalValue.GetType();
object clonedValue = Activator.CreateInstance(originalType);
deepCopyReferenceTypeFieldBased(originalType, originalValue, clonedValue);
fieldInfo.SetValue(target, clonedValue);
}
}
}
}
/// <summary>Creates a property-based deep copy of a reference type</summary>
/// <param name="copiedType">Type the copy will be based upon</param>
/// <param name="original">Original object that will be copied</param>
/// <param name="target">Target object into which copied values will be written</param>
private static void deepCopyReferenceTypePropertyBased(
Type copiedType, object original, object target
) {
PropertyInfo[] propertyInfos = copiedType.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) {
if(propertyInfo.PropertyType.IsPrimitive) {
propertyInfo.SetValue(
target,
propertyInfo.GetValue(original, null),
null
);
} else if(propertyInfo.PropertyType.IsValueType) {
object boxedOriginalValue = propertyInfo.GetValue(original, null);
Type originalType = boxedOriginalValue.GetType();
object boxedClonedValue = Activator.CreateInstance(originalType);
deepCopyReferenceTypePropertyBased(
originalType, boxedOriginalValue, boxedClonedValue
);
propertyInfo.SetValue(target, boxedClonedValue, null);
} else {
object originalValue = propertyInfo.GetValue(original, null);
if(originalValue != null) {
Type originalType = originalValue.GetType();
object clonedValue = Activator.CreateInstance(originalType);
deepCopyReferenceTypePropertyBased(
originalType, originalValue, clonedValue
);
propertyInfo.SetValue(target, clonedValue, null);
}
}
}
}
}
/// <summary>Creates a field-based deep copy of a value type</summary>
/// <typeparam name="TState">Value type that will be copied</typeparam>
/// <param name="original">Original object that will be copied</param>
/// <param name="target">Target object into which copied values will be written</param>
private static void deepCopyValueTypeFieldBased<TState>(
ref TState original, ref TState target
) {
object boxedOriginal = original;
object boxedTarget = Activator.CreateInstance(original.GetType());
FieldInfo[] fieldInfos = typeof(TState).GetFields(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy
);
for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index];
if(fieldInfo.FieldType.IsPrimitive) {
object boxedValue = fieldInfo.GetValue(boxedOriginal);
fieldInfo.SetValue(boxedTarget, boxedValue);
} else if(fieldInfo.FieldType.IsValueType) {
object boxedOriginalValue = fieldInfo.GetValue(boxedOriginal);
Type originalType = boxedOriginalValue.GetType();
object boxedClonedValue = Activator.CreateInstance(originalType);
deepCopyReferenceTypeFieldBased(originalType, boxedOriginalValue, boxedClonedValue);
fieldInfo.SetValue(boxedTarget, boxedClonedValue);
} else {
object originalValue = fieldInfo.GetValue(boxedOriginal);
if(originalValue != null) {
Type originalType = originalValue.GetType();
object clonedValue = Activator.CreateInstance(originalType);
deepCopyReferenceTypeFieldBased(originalType, originalValue, clonedValue);
fieldInfo.SetValue(boxedTarget, clonedValue);
}
}
}
target = (TState)boxedTarget;
}
/// <summary>Creates a property-based shallow copy of a value type</summary>
/// <typeparam name="TState">Value type that will be copied</typeparam>
/// <param name="original">Original object that will be copied</param>
/// <param name="target">Target object into which copied values will be written</param>
private static void deepCopyValueTypePropertyBased<TState>(
ref TState original, ref TState target
) {
object boxedOriginal = original;
object boxedTarget = Activator.CreateInstance(original.GetType());
PropertyInfo[] propertyInfos = typeof(TState).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) {
if(propertyInfo.PropertyType.IsPrimitive) {
propertyInfo.SetValue(
boxedTarget,
propertyInfo.GetValue(boxedOriginal, null),
null
);
} else if(propertyInfo.PropertyType.IsValueType) {
object boxedOriginalValue = propertyInfo.GetValue(boxedOriginal, null);
Type originalType = boxedOriginalValue.GetType();
object boxedClonedValue = Activator.CreateInstance(originalType);
deepCopyReferenceTypePropertyBased(
originalType, boxedOriginalValue, boxedClonedValue
);
propertyInfo.SetValue(boxedTarget, boxedClonedValue, null);
} else {
object originalValue = propertyInfo.GetValue(boxedOriginal, null);
if(originalValue != null) {
Type originalType = originalValue.GetType();
object clonedValue = Activator.CreateInstance(originalType);
deepCopyReferenceTypePropertyBased(originalType, originalValue, clonedValue);
propertyInfo.SetValue(boxedTarget, clonedValue, null);
}
}
}
}
target = (TState)boxedTarget;
}
}
} // namespace Nuclex.Support.Cloning

View File

@ -47,7 +47,7 @@ namespace Nuclex.Support.IO {
/// <summary>Initializes a new stream chainer</summary> /// <summary>Initializes a new stream chainer</summary>
/// <param name="streams">Array of streams that will be chained together</param> /// <param name="streams">Array of streams that will be chained together</param>
public ChainStream(Stream[] streams) { public ChainStream(params Stream[] streams) {
this.streams = (Stream[])streams.Clone(); this.streams = (Stream[])streams.Clone();
determineCapabilities(); determineCapabilities();

View File

@ -56,7 +56,7 @@ namespace Nuclex.Support {
/// become eligible for execution. /// become eligible for execution.
/// </para> /// </para>
/// </remarks> /// </remarks>
#if !XBOX360 #if !(XBOX360 || WINDOWS_PHONE)
[Obsolete("Prefer the normal semaphore on Windows builds.")] [Obsolete("Prefer the normal semaphore on Windows builds.")]
#endif #endif
public class Semaphore : WaitHandle { public class Semaphore : WaitHandle {