Expression tree cloner can now clone primitive types

git-svn-id: file:///srv/devel/repo-conversion/nusu@229 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2012-02-03 16:10:21 +00:00
parent 976b1ddba5
commit 776a0749fd
2 changed files with 188 additions and 98 deletions

View File

@ -40,10 +40,22 @@ namespace Nuclex.Support.Cloning {
[Test]
public void PrimitiveTypesCanBeCloned() {
int original = 12345;
int clone = this.cloneFactory.ShallowClone(original, false);
int clone = this.cloneFactory.DeepClone(original, false);
Assert.AreEqual(original, clone);
}
/// <summary>Verifies that shallow clones of arrays can be made</summary>
[Test]
public void ReferenceTypesCanBeCloned() {
var original = new TestReferenceType() { TestField = 123, TestProperty = 456 };
TestReferenceType clone = this.cloneFactory.DeepClone(original, false);
Assert.AreNotSame(original, clone);
//Assert.AreEqual(original.TestField, clone.TestField);
//Assert.AreEqual(original.TestProperty, clone.TestProperty);
}
#if false
/// <summary>Verifies that shallow clones of arrays can be made</summary>
[Test]
public void ShallowClonesOfArraysCanBeMade() {
@ -166,6 +178,7 @@ namespace Nuclex.Support.Cloning {
HierarchicalReferenceType clone = this.cloneFactory.DeepClone(original, true);
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
}
#endif
/// <summary>Clone factory being tested</summary>
private ICloneFactory cloneFactory;

View File

@ -22,6 +22,9 @@ License along with this library
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Linq.Expressions;
using System.Collections.Generic;
namespace Nuclex.Support.Cloning {
@ -59,7 +62,12 @@ namespace Nuclex.Support.Cloning {
public static TCloned DeepClone<TCloned>(
TCloned objectToClone, bool usePropertyBasedClone
) {
if(usePropertyBasedClone) {
throw new NotImplementedException("Not implemented yet");
} else {
Func<object, object> cloner = getOrCreateDeepFieldBasedCloner(typeof(TCloned));
return (TCloned)cloner(objectToClone);
}
}
/// <summary>
@ -108,6 +116,75 @@ namespace Nuclex.Support.Cloning {
return ExpressionTreeCloner.ShallowClone<TCloned>(objectToClone, usePropertyBasedClone);
}
/// <summary>
/// Retrieves the existing clone method for the specified type or compiles one if
/// none exists for the type yet
/// </summary>
/// <param name="type">Type for which a clone method will be retrieved</param>
/// <returns>The clone method for the specified type</returns>
private static Func<object, object> getOrCreateDeepFieldBasedCloner(Type type) {
Func<object, object> cloner;
if(!deepCloners.TryGetValue(type, out cloner)) {
cloner = createDeepFieldBasedCloner(type);
deepCloners.TryAdd(type, cloner);
}
return cloner;
}
/// <summary>Compiles a method that creates a clone of an object</summary>
/// <param name="type">Type for which a clone method will be created</param>
/// <returns>A method that clones an object of the provided type</returns>
private static Func<object, object> createDeepFieldBasedCloner(Type type) {
FieldInfo[] fieldInfos = type.GetFields(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy
);
ParameterExpression original = Expression.Parameter(typeof(object), "original");
ParameterExpression clone = Expression.Variable(typeof(object), "clone");
//ParameterExpression typedOriginal = Expression.Variable(type, "typedOriginal");
var transferExpressions = new List<Expression>();
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;
if(fieldType.IsPrimitive) {
transferExpressions.Add(
Expression.Assign(
Expression.Field(clone, fieldInfo),
Expression.Field(typedOriginal, fieldInfo)
)
);
}
}
*/
}
Expression<Func<object, object>> expression =
Expression.Lambda<Func<object, object>>(
Expression.Block(
new[] { clone },
transferExpressions
),
original
);
return expression.Compile();
}
#if false
/// <summary>
/// Transfers the state of one object into another, creating clones of referenced objects