Cloners now offer their methods as static members for easy access in non-service oriented applications; began implementing the expression tree cloner

git-svn-id: file:///srv/devel/repo-conversion/nusu@228 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2012-02-03 14:49:31 +00:00
parent 961f56157e
commit 976b1ddba5
8 changed files with 323 additions and 43 deletions

View File

@ -63,6 +63,9 @@
<Compile Include="Source\Cloning\CloneFactoryTest.cs" /> <Compile Include="Source\Cloning\CloneFactoryTest.cs" />
<Compile Include="Source\Cloning\CloningParameters.cs" /> <Compile Include="Source\Cloning\CloningParameters.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.cs" /> <Compile Include="Source\Cloning\ExpressionTreeCloner.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.Test.cs">
<DependentUpon>ExpressionTreeCloner.cs</DependentUpon>
</Compile>
<Compile Include="Source\Cloning\IStateCopier.cs" /> <Compile Include="Source\Cloning\IStateCopier.cs" />
<Compile Include="Source\Cloning\ReflectionCloner.cs" /> <Compile Include="Source\Cloning\ReflectionCloner.cs" />
<Compile Include="Source\Cloning\ReflectionCloner.Test.cs"> <Compile Include="Source\Cloning\ReflectionCloner.Test.cs">

View File

@ -94,6 +94,9 @@
<Compile Include="Source\Cloning\CloneFactoryTest.cs" /> <Compile Include="Source\Cloning\CloneFactoryTest.cs" />
<Compile Include="Source\Cloning\CloningParameters.cs" /> <Compile Include="Source\Cloning\CloningParameters.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.cs" /> <Compile Include="Source\Cloning\ExpressionTreeCloner.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.Test.cs">
<DependentUpon>ExpressionTreeCloner.cs</DependentUpon>
</Compile>
<Compile Include="Source\Cloning\ICloneFactory.cs" /> <Compile Include="Source\Cloning\ICloneFactory.cs" />
<Compile Include="Source\Cloning\IStateCopier.cs" /> <Compile Include="Source\Cloning\IStateCopier.cs" />
<Compile Include="Source\Cloning\ReflectionCloner.cs" /> <Compile Include="Source\Cloning\ReflectionCloner.cs" />

View File

@ -105,6 +105,9 @@
<Compile Include="Source\Cloning\CloneFactoryTest.cs" /> <Compile Include="Source\Cloning\CloneFactoryTest.cs" />
<Compile Include="Source\Cloning\CloningParameters.cs" /> <Compile Include="Source\Cloning\CloningParameters.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.cs" /> <Compile Include="Source\Cloning\ExpressionTreeCloner.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.Test.cs">
<DependentUpon>ExpressionTreeCloner.cs</DependentUpon>
</Compile>
<Compile Include="Source\Cloning\ICloneFactory.cs" /> <Compile Include="Source\Cloning\ICloneFactory.cs" />
<Compile Include="Source\Cloning\IStateCopier.cs" /> <Compile Include="Source\Cloning\IStateCopier.cs" />
<Compile Include="Source\Cloning\ReflectionCloner.cs" /> <Compile Include="Source\Cloning\ReflectionCloner.cs" />

View File

@ -0,0 +1,177 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2010 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
#if UNITTEST
using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace Nuclex.Support.Cloning {
/// <summary>Unit Test for the expression tree-based cloner</summary>
[TestFixture]
public class ExpressionTreeClonerTest : CloneFactoryTest {
/// <summary>Initializes a new unit test suite for the reflection cloner</summary>
public ExpressionTreeClonerTest() {
this.cloneFactory = new ExpressionTreeCloner();
}
/// <summary>Verifies that clones of primitive types can be created</summary>
[Test]
public void PrimitiveTypesCanBeCloned() {
int original = 12345;
int clone = this.cloneFactory.ShallowClone(original, false);
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.ShallowClone(original, false);
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.DeepClone(original, false);
Assert.AreNotSame(original[0], clone[0]);
Assert.AreEqual(original[0].TestField, clone[0].TestField);
Assert.AreEqual(original[0].TestProperty, clone[0].TestProperty);
}
/// <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.DeepClone(original, false);
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.DeepClone(original, false);
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.ShallowClone(original, false);
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.ShallowClone(original, false);
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.DeepClone(original, false);
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.DeepClone(original, false);
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.ShallowClone(original, true);
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.ShallowClone(original, true);
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.DeepClone(original, true);
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.DeepClone(original, true);
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
}
/// <summary>Clone factory being tested</summary>
private ICloneFactory cloneFactory;
}
} // namespace Nuclex.Support.Cloning
#endif // UNITTEST

View File

@ -18,15 +18,13 @@ License along with this library
*/ */
#endregion #endregion
#if !(XBOX360 || WINDOWS_PHONE)
using System; using System;
using System.Collections.Generic; using System.Collections.Concurrent;
using System.Linq;
using System.Text;
namespace Nuclex.Support.Cloning { namespace Nuclex.Support.Cloning {
#if false
/// <summary>An action that takes its arguments as references to a structure</summary> /// <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="TFirst">Type of the first argument to the method</typeparam>
/// <typeparam name="TSecond">Type of the second argument to the method</typeparam> /// <typeparam name="TSecond">Type of the second argument to the method</typeparam>
@ -40,7 +38,13 @@ namespace Nuclex.Support.Cloning {
/// Cloning factory which uses expression trees to improve performance when cloning /// Cloning factory which uses expression trees to improve performance when cloning
/// is a high-frequency action. /// is a high-frequency action.
/// </summary> /// </summary>
public class ExpressionTreeCloneFactory : ICloneFactory { public class ExpressionTreeCloner : ICloneFactory {
/// <summary>Initializes the static members of the expression tree cloner</summary>
static ExpressionTreeCloner() {
shallowCloners = new ConcurrentDictionary<Type, Func<object, object>>();
deepCloners = new ConcurrentDictionary<Type, Func<object, object>>();
}
/// <summary> /// <summary>
/// Creates a deep clone of the specified object, also creating clones of all /// Creates a deep clone of the specified object, also creating clones of all
@ -52,9 +56,10 @@ namespace Nuclex.Support.Cloning {
/// Whether to clone the object based on its properties only /// Whether to clone the object based on its properties only
/// </param> /// </param>
/// <returns>A deep clone of the provided object</returns> /// <returns>A deep clone of the provided object</returns>
public TCloned DeepClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone) public static TCloned DeepClone<TCloned>(
where TCloned : new() { TCloned objectToClone, bool usePropertyBasedClone
throw new NotImplementedException(); ) {
throw new NotImplementedException("Not implemented yet");
} }
/// <summary> /// <summary>
@ -66,11 +71,44 @@ namespace Nuclex.Support.Cloning {
/// Whether to clone the object based on its properties only /// Whether to clone the object based on its properties only
/// </param> /// </param>
/// <returns>A shallow clone of the provided object</returns> /// <returns>A shallow clone of the provided object</returns>
public TCloned ShallowClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone) public static TCloned ShallowClone<TCloned>(
where TCloned : new() { TCloned objectToClone, bool usePropertyBasedClone
throw new NotImplementedException(); ) {
throw new NotImplementedException("Not implemented yet");
} }
/// <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>
TCloned ICloneFactory.DeepClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone
) {
return ExpressionTreeCloner.DeepClone<TCloned>(objectToClone, 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>
TCloned ICloneFactory.ShallowClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone
) {
return ExpressionTreeCloner.ShallowClone<TCloned>(objectToClone, usePropertyBasedClone);
}
#if false
/// <summary> /// <summary>
/// Transfers the state of one object into another, creating clones of referenced objects /// Transfers the state of one object into another, creating clones of referenced objects
/// </summary> /// </summary>
@ -145,9 +183,15 @@ namespace Nuclex.Support.Cloning {
where TCloned : class, new() { where TCloned : class, new() {
throw new NotImplementedException(); throw new NotImplementedException();
} }
#endif
/// <summary>Compiled cloners that perform shallow clone operations</summary>
private static ConcurrentDictionary<Type, Func<object, object>> shallowCloners;
/// <summary>Compiled cloners that perform deep clone operations</summary>
private static ConcurrentDictionary<Type, Func<object, object>> deepCloners;
} }
#endif
} // namespace Nuclex.Support.Cloning } // namespace Nuclex.Support.Cloning
#endif // !(XBOX360 || WINDOWS_PHONE)

View File

@ -42,7 +42,9 @@ namespace Nuclex.Support.Cloning {
/// Whether to clone the object based on its properties only /// Whether to clone the object based on its properties only
/// </param> /// </param>
/// <returns>A shallow clone of the provided object</returns> /// <returns>A shallow clone of the provided object</returns>
public TCloned ShallowClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone) { public static TCloned ShallowClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone
) {
Type originalType = objectToClone.GetType(); Type originalType = objectToClone.GetType();
if(originalType.IsPrimitive || (originalType == typeof(string))) { if(originalType.IsPrimitive || (originalType == typeof(string))) {
return objectToClone; // Being value types, primitives are copied by default return objectToClone; // Being value types, primitives are copied by default
@ -73,7 +75,9 @@ namespace Nuclex.Support.Cloning {
/// Whether to clone the object based on its properties only /// Whether to clone the object based on its properties only
/// </param> /// </param>
/// <returns>A deep clone of the provided object</returns> /// <returns>A deep clone of the provided object</returns>
public TCloned DeepClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone) { public static TCloned DeepClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone
) {
if(usePropertyBasedClone) { if(usePropertyBasedClone) {
return (TCloned)deepCloneSinglePropertyBased(objectToClone); return (TCloned)deepCloneSinglePropertyBased(objectToClone);
} else { } else {
@ -81,10 +85,39 @@ namespace Nuclex.Support.Cloning {
} }
} }
/// <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>
TCloned ICloneFactory.ShallowClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone
) {
return ReflectionCloner.ShallowClone<TCloned>(objectToClone, usePropertyBasedClone);
}
/// <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>
TCloned ICloneFactory.DeepClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone) {
return ReflectionCloner.DeepClone<TCloned>(objectToClone, usePropertyBasedClone);
}
/// <summary>Clones a complex type using field-based value transfer</summary> /// <summary>Clones a complex type using field-based value transfer</summary>
/// <param name="original">Original instance that will be cloned</param> /// <param name="original">Original instance that will be cloned</param>
/// <returns>A clone of the original instance</returns> /// <returns>A clone of the original instance</returns>
private object shallowCloneComplexFieldBased(object original) { private static object shallowCloneComplexFieldBased(object original) {
Type originalType = original.GetType(); Type originalType = original.GetType();
object clone = Activator.CreateInstance(originalType); object clone = Activator.CreateInstance(originalType);
@ -107,7 +140,7 @@ namespace Nuclex.Support.Cloning {
/// <summary>Clones a complex type using property-based value transfer</summary> /// <summary>Clones a complex type using property-based value transfer</summary>
/// <param name="original">Original instance that will be cloned</param> /// <param name="original">Original instance that will be cloned</param>
/// <returns>A clone of the original instance</returns> /// <returns>A clone of the original instance</returns>
private object shallowCloneComplexPropertyBased(object original) { private static object shallowCloneComplexPropertyBased(object original) {
Type originalType = original.GetType(); Type originalType = original.GetType();
object clone = Activator.CreateInstance(originalType); object clone = Activator.CreateInstance(originalType);
@ -140,7 +173,7 @@ namespace Nuclex.Support.Cloning {
/// <summary>Clones an array using field-based value transfer</summary> /// <summary>Clones an array using field-based value transfer</summary>
/// <param name="original">Original array that will be cloned</param> /// <param name="original">Original array that will be cloned</param>
/// <returns>A clone of the original array</returns> /// <returns>A clone of the original array</returns>
private object shallowCloneArray(object original) { private static object shallowCloneArray(object original) {
return ((Array)original).Clone(); return ((Array)original).Clone();
} }

View File

@ -27,7 +27,7 @@ using NUnit.Framework;
namespace Nuclex.Support.Cloning { namespace Nuclex.Support.Cloning {
/// <summary>Unit Test for the reflection-based cloner</summary> /// <summary>Unit Test for the binary serializer-based cloner</summary>
[TestFixture] [TestFixture]
public class SerializationClonerTest : CloneFactoryTest { public class SerializationClonerTest : CloneFactoryTest {

View File

@ -18,6 +18,8 @@ License along with this library
*/ */
#endregion #endregion
#if !(XBOX360 || WINDOWS_PHONE)
using System; using System;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
@ -222,22 +224,15 @@ namespace Nuclex.Support.Cloning {
#endregion // class PropertySerializationSurrogate #endregion // class PropertySerializationSurrogate
/// <summary>Initializes a new serialization-based cloner</summary> /// <summary>Initializes the static members of the serialization-based cloner</summary>
public SerializationCloner() { static SerializationCloner() {
var fieldSurrogateSelector = new SurrogateSelector(); fieldBasedFormatter = new BinaryFormatter(
fieldSurrogateSelector.ChainSelector( new StaticSurrogateSelector(new FieldSerializationSurrogate()),
new StaticSurrogateSelector(new FieldSerializationSurrogate()) new StreamingContext(StreamingContextStates.All)
); );
this.fieldBasedFormatter = new BinaryFormatter( propertyBasedFormatter = new BinaryFormatter(
fieldSurrogateSelector, new StreamingContext(StreamingContextStates.All) new StaticSurrogateSelector(new PropertySerializationSurrogate()),
); new StreamingContext(StreamingContextStates.All)
var propertySurrogateSelector = new SurrogateSelector();
propertySurrogateSelector.ChainSelector(
new StaticSurrogateSelector(new PropertySerializationSurrogate())
);
this.propertyBasedFormatter = new BinaryFormatter(
propertySurrogateSelector, new StreamingContext(StreamingContextStates.All)
); );
} }
@ -251,20 +246,38 @@ namespace Nuclex.Support.Cloning {
/// Whether to clone the object based on its properties only /// Whether to clone the object based on its properties only
/// </param> /// </param>
/// <returns>A deep clone of the provided object</returns> /// <returns>A deep clone of the provided object</returns>
public TCloned DeepClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone) { public static TCloned DeepClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone
) {
using(var memoryStream = new MemoryStream()) { using(var memoryStream = new MemoryStream()) {
if(usePropertyBasedClone) { if(usePropertyBasedClone) {
this.propertyBasedFormatter.Serialize(memoryStream, objectToClone); propertyBasedFormatter.Serialize(memoryStream, objectToClone);
memoryStream.Position = 0; memoryStream.Position = 0;
return (TCloned)this.propertyBasedFormatter.Deserialize(memoryStream); return (TCloned)propertyBasedFormatter.Deserialize(memoryStream);
} else { } else {
this.fieldBasedFormatter.Serialize(memoryStream, objectToClone); fieldBasedFormatter.Serialize(memoryStream, objectToClone);
memoryStream.Position = 0; memoryStream.Position = 0;
return (TCloned)this.fieldBasedFormatter.Deserialize(memoryStream); 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>
/// <param name="usePropertyBasedClone">
/// Whether to clone the object based on its properties only
/// </param>
/// <returns>A deep clone of the provided object</returns>
TCloned ICloneFactory.DeepClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone
) {
return SerializationCloner.DeepClone<TCloned>(objectToClone, usePropertyBasedClone);
}
/// <summary> /// <summary>
/// Creates a shallow clone of the specified object, reusing any referenced objects /// Creates a shallow clone of the specified object, reusing any referenced objects
/// </summary> /// </summary>
@ -274,15 +287,19 @@ namespace Nuclex.Support.Cloning {
/// Whether to clone the object based on its properties only /// Whether to clone the object based on its properties only
/// </param> /// </param>
/// <returns>A shallow clone of the provided object</returns> /// <returns>A shallow clone of the provided object</returns>
public TCloned ShallowClone<TCloned>(TCloned objectToClone, bool usePropertyBasedClone) { TCloned ICloneFactory.ShallowClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone
) {
throw new NotSupportedException("The serialization cloner cannot create shallow clones"); throw new NotSupportedException("The serialization cloner cannot create shallow clones");
} }
/// <summary>Serializes objects by storing their fields</summary> /// <summary>Serializes objects by storing their fields</summary>
private BinaryFormatter fieldBasedFormatter; private static BinaryFormatter fieldBasedFormatter;
/// <summary>Serializes objects by storing their properties</summary> /// <summary>Serializes objects by storing their properties</summary>
private BinaryFormatter propertyBasedFormatter; private static BinaryFormatter propertyBasedFormatter;
} }
} // namespace Nuclex.Support.Cloning } // namespace Nuclex.Support.Cloning
#endif // !(XBOX360 || WINDOWS_PHONE)