From 4743b5d056dcdd1aa15d8aca8a76a8eca2513279 Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Fri, 3 Feb 2012 18:06:46 +0000 Subject: [PATCH] Expression tree cloner now is able to clone complex types (non-recursively, at the moment) git-svn-id: file:///srv/devel/repo-conversion/nusu@230 d2e56fa2-650e-0410-a79f-9358c0239efd --- Source/Cloning/ExpressionTreeCloner.Test.cs | 52 ++-- Source/Cloning/ExpressionTreeCloner.cs | 292 ++++++++++---------- 2 files changed, 172 insertions(+), 172 deletions(-) diff --git a/Source/Cloning/ExpressionTreeCloner.Test.cs b/Source/Cloning/ExpressionTreeCloner.Test.cs index f8461b7..98a76f8 100644 --- a/Source/Cloning/ExpressionTreeCloner.Test.cs +++ b/Source/Cloning/ExpressionTreeCloner.Test.cs @@ -27,33 +27,33 @@ using NUnit.Framework; namespace Nuclex.Support.Cloning { - /// Unit Test for the expression tree-based cloner - [TestFixture] - public class ExpressionTreeClonerTest : CloneFactoryTest { + /// Unit Test for the expression tree-based cloner + [TestFixture] + public class ExpressionTreeClonerTest : CloneFactoryTest { - /// Initializes a new unit test suite for the reflection cloner - public ExpressionTreeClonerTest() { - this.cloneFactory = new ExpressionTreeCloner(); - } + /// Initializes a new unit test suite for the reflection cloner + public ExpressionTreeClonerTest() { + this.cloneFactory = new ExpressionTreeCloner(); + } - /// Verifies that clones of primitive types can be created - [Test] - public void PrimitiveTypesCanBeCloned() { - int original = 12345; - int clone = this.cloneFactory.DeepClone(original, false); - Assert.AreEqual(original, clone); - } + /// Verifies that clones of primitive types can be created + [Test] + public void PrimitiveTypesCanBeCloned() { + int original = 12345; + int clone = this.cloneFactory.DeepClone(original, false); + Assert.AreEqual(original, clone); + } - /// Verifies that shallow clones of arrays can be made - [Test] - public void ReferenceTypesCanBeCloned() { - var original = new TestReferenceType() { TestField = 123, TestProperty = 456 }; - TestReferenceType clone = this.cloneFactory.DeepClone(original, false); + /// Verifies that shallow clones of arrays can be made + [Test] + public void ReferenceTypesCanBeCloned() { + var original = new TestReferenceType() { TestField = 123, TestProperty = 456 }; + TestReferenceType clone = this.cloneFactory.DeepClone(original, false); - Assert.AreNotSame(original, clone); - //Assert.AreEqual(original.TestField, clone.TestField); - //Assert.AreEqual(original.TestProperty, clone.TestProperty); - } + Assert.AreNotSame(original, clone); + Assert.AreEqual(original.TestField, clone.TestField); + Assert.AreEqual(original.TestProperty, clone.TestProperty); + } #if false /// Verifies that shallow clones of arrays can be made @@ -180,10 +180,10 @@ namespace Nuclex.Support.Cloning { } #endif - /// Clone factory being tested - private ICloneFactory cloneFactory; + /// Clone factory being tested + private ICloneFactory cloneFactory; - } + } } // namespace Nuclex.Support.Cloning diff --git a/Source/Cloning/ExpressionTreeCloner.cs b/Source/Cloning/ExpressionTreeCloner.cs index 1e0d38a..2a67817 100644 --- a/Source/Cloning/ExpressionTreeCloner.cs +++ b/Source/Cloning/ExpressionTreeCloner.cs @@ -28,162 +28,162 @@ using System.Collections.Generic; namespace Nuclex.Support.Cloning { - /// An action that takes its arguments as references to a structure - /// Type of the first argument to the method - /// Type of the second argument to the method - /// First argument to the method - /// Second argument to the method - public delegate void ReferenceAction(ref TFirst first, ref TSecond second) - where TFirst : struct - where TSecond : struct; + /// An action that takes its arguments as references to a structure + /// Type of the first argument to the method + /// Type of the second argument to the method + /// First argument to the method + /// Second argument to the method + public delegate void ReferenceAction(ref TFirst first, ref TSecond second) + where TFirst : struct + where TSecond : struct; - /// - /// Cloning factory which uses expression trees to improve performance when cloning - /// is a high-frequency action. - /// - public class ExpressionTreeCloner : ICloneFactory { + /// + /// Cloning factory which uses expression trees to improve performance when cloning + /// is a high-frequency action. + /// + public class ExpressionTreeCloner : ICloneFactory { - /// Initializes the static members of the expression tree cloner - static ExpressionTreeCloner() { - shallowCloners = new ConcurrentDictionary>(); - deepCloners = new ConcurrentDictionary>(); - } + /// Initializes the static members of the expression tree cloner + static ExpressionTreeCloner() { + shallowCloners = new ConcurrentDictionary(); + deepCloners = new ConcurrentDictionary(); + } - /// - /// Creates a deep clone of the specified object, also creating clones of all - /// child objects being referenced - /// - /// Type of the object that will be cloned - /// Object that will be cloned - /// - /// Whether to clone the object based on its properties only - /// - /// A deep clone of the provided object - public static TCloned DeepClone( - TCloned objectToClone, bool usePropertyBasedClone - ) { - if(usePropertyBasedClone) { - throw new NotImplementedException("Not implemented yet"); - } else { - Func cloner = getOrCreateDeepFieldBasedCloner(typeof(TCloned)); - return (TCloned)cloner(objectToClone); - } - } + /// + /// Creates a deep clone of the specified object, also creating clones of all + /// child objects being referenced + /// + /// Type of the object that will be cloned + /// Object that will be cloned + /// + /// Whether to clone the object based on its properties only + /// + /// A deep clone of the provided object + public static TCloned DeepClone( + TCloned objectToClone, bool usePropertyBasedClone + ) { + if(usePropertyBasedClone) { + throw new NotImplementedException("Not implemented yet"); + } else { + Func cloner = getOrCreateDeepFieldBasedCloner(); + return cloner(objectToClone); + } + } - /// - /// Creates a shallow clone of the specified object, reusing any referenced objects - /// - /// Type of the object that will be cloned - /// Object that will be cloned - /// - /// Whether to clone the object based on its properties only - /// - /// A shallow clone of the provided object - public static TCloned ShallowClone( - TCloned objectToClone, bool usePropertyBasedClone - ) { - throw new NotImplementedException("Not implemented yet"); - } + /// + /// Creates a shallow clone of the specified object, reusing any referenced objects + /// + /// Type of the object that will be cloned + /// Object that will be cloned + /// + /// Whether to clone the object based on its properties only + /// + /// A shallow clone of the provided object + public static TCloned ShallowClone( + TCloned objectToClone, bool usePropertyBasedClone + ) { + throw new NotImplementedException("Not implemented yet"); + } - /// - /// Creates a deep clone of the specified object, also creating clones of all - /// child objects being referenced - /// - /// Type of the object that will be cloned - /// Object that will be cloned - /// - /// Whether to clone the object based on its properties only - /// - /// A deep clone of the provided object - TCloned ICloneFactory.DeepClone( - TCloned objectToClone, bool usePropertyBasedClone - ) { - return ExpressionTreeCloner.DeepClone(objectToClone, usePropertyBasedClone); - } + /// + /// Creates a deep clone of the specified object, also creating clones of all + /// child objects being referenced + /// + /// Type of the object that will be cloned + /// Object that will be cloned + /// + /// Whether to clone the object based on its properties only + /// + /// A deep clone of the provided object + TCloned ICloneFactory.DeepClone( + TCloned objectToClone, bool usePropertyBasedClone + ) { + return ExpressionTreeCloner.DeepClone(objectToClone, usePropertyBasedClone); + } - /// - /// Creates a shallow clone of the specified object, reusing any referenced objects - /// - /// Type of the object that will be cloned - /// Object that will be cloned - /// - /// Whether to clone the object based on its properties only - /// - /// A shallow clone of the provided object - TCloned ICloneFactory.ShallowClone( - TCloned objectToClone, bool usePropertyBasedClone - ) { - return ExpressionTreeCloner.ShallowClone(objectToClone, usePropertyBasedClone); - } + /// + /// Creates a shallow clone of the specified object, reusing any referenced objects + /// + /// Type of the object that will be cloned + /// Object that will be cloned + /// + /// Whether to clone the object based on its properties only + /// + /// A shallow clone of the provided object + TCloned ICloneFactory.ShallowClone( + TCloned objectToClone, bool usePropertyBasedClone + ) { + return ExpressionTreeCloner.ShallowClone(objectToClone, usePropertyBasedClone); + } - /// - /// Retrieves the existing clone method for the specified type or compiles one if - /// none exists for the type yet - /// - /// Type for which a clone method will be retrieved - /// The clone method for the specified type - private static Func getOrCreateDeepFieldBasedCloner(Type type) { - Func cloner; - if(!deepCloners.TryGetValue(type, out cloner)) { - cloner = createDeepFieldBasedCloner(type); - deepCloners.TryAdd(type, cloner); - } + /// + /// Retrieves the existing clone method for the specified type or compiles one if + /// none exists for the type yet + /// + /// Type for which a clone method will be retrieved + /// The clone method for the specified type + private static Func getOrCreateDeepFieldBasedCloner() { + Type clonedType = typeof(TCloned); + Delegate clonerAsDelegate; + if(deepCloners.TryGetValue(clonedType, out clonerAsDelegate)) { + return (Func)clonerAsDelegate; + } else { + Func cloner = createDeepFieldBasedCloner(); + deepCloners.TryAdd(clonedType, cloner); + return cloner; + } + } - return cloner; - } + /// Compiles a method that creates a clone of an object + /// Type for which a clone method will be created + /// A method that clones an object of the provided type + private static Func createDeepFieldBasedCloner() { + Type clonedType = typeof(TCloned); + FieldInfo[] fieldInfos = clonedType.GetFields( + BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.Instance | BindingFlags.FlattenHierarchy + ); - /// Compiles a method that creates a clone of an object - /// Type for which a clone method will be created - /// A method that clones an object of the provided type - private static Func createDeepFieldBasedCloner(Type type) { - FieldInfo[] fieldInfos = type.GetFields( - BindingFlags.Public | BindingFlags.NonPublic | - BindingFlags.Instance | BindingFlags.FlattenHierarchy - ); + ParameterExpression original = Expression.Parameter(typeof(TCloned), "original"); + ParameterExpression clone = Expression.Variable(typeof(TCloned), "clone"); - ParameterExpression original = Expression.Parameter(typeof(object), "original"); - ParameterExpression clone = Expression.Variable(typeof(object), "clone"); - //ParameterExpression typedOriginal = Expression.Variable(type, "typedOriginal"); + var transferExpressions = new List(); - var transferExpressions = new List(); + if(clonedType.IsPrimitive || (clonedType == typeof(string))) { + transferExpressions.Add(Expression.Assign(clone, original)); + } else if(clonedType.IsArray) { + throw new NotImplementedException("Not implemented yet"); + } else { + transferExpressions.Add(Expression.Assign(clone, Expression.New(clonedType))); - if(type.IsPrimitive || (type == typeof(string))) { - transferExpressions.Add(Expression.Assign(clone, original)); - } else if(type.IsArray) { - //throw new NotImplementedException("Not implemented yet"); - } else { - transferExpressions.Add(Expression.Assign(clone, Expression.New(type))); - /* - transferExpressions.Add( - Expression.Assign(typedOriginal, Expression.Convert(original, type)) - ); - for(int index = 0; index < fieldInfos.Length; ++index) { - FieldInfo fieldInfo = fieldInfos[index]; - Type fieldType = fieldInfo.FieldType; + for(int index = 0; index < fieldInfos.Length; ++index) { + FieldInfo fieldInfo = fieldInfos[index]; + Type fieldType = fieldInfo.FieldType; - if(fieldType.IsPrimitive) { - transferExpressions.Add( - Expression.Assign( - Expression.Field(clone, fieldInfo), - Expression.Field(typedOriginal, fieldInfo) - ) - ); - } - } - */ - } + if(fieldType.IsPrimitive) { + transferExpressions.Add( + Expression.Assign( + Expression.Field(clone, fieldInfo), + Expression.Field(original, fieldInfo) + ) + ); + } + } - Expression> expression = - Expression.Lambda>( - Expression.Block( - new[] { clone }, - transferExpressions - ), - original - ); + transferExpressions.Add(clone); + } - return expression.Compile(); - } + Expression> expression = + Expression.Lambda>( + Expression.Block( + new[] { clone }, + transferExpressions + ), + original + ); + + return expression.Compile(); + } #if false /// @@ -262,12 +262,12 @@ namespace Nuclex.Support.Cloning { } #endif - /// Compiled cloners that perform shallow clone operations - private static ConcurrentDictionary> shallowCloners; - /// Compiled cloners that perform deep clone operations - private static ConcurrentDictionary> deepCloners; + /// Compiled cloners that perform shallow clone operations + private static ConcurrentDictionary shallowCloners; + /// Compiled cloners that perform deep clone operations + private static ConcurrentDictionary deepCloners; - } + } } // namespace Nuclex.Support.Cloning