you can download this here: Download

(download is the console app used to write this page)

A few years ago, I threw together the DeepCloneInjection, it was good, a lot of people needed it, and some modified it for to perform different tasks such as filling an object tree with data from another. It had a few problems though and also I thought that it could have been made a lot faster by using the SmartConventionInjection and the FastMember library

So here it is, the new DeepCloneInjection:

Usage:
   var clone = new Foo().InjectFrom<DeepCloneInjection>(foo);

Speed Test:
// 9000 Iterations
DeepCloneInjection = 00:00:01.8209
DeepCloneInjection using FastMember = 00:00:01.6848
Old DeepClone Injection = 00:00:06.1544
// note, depending one the cloned type the difference between these numbers can be much bigger

DeepCloneInjection:
    public class DeepCloneInjection : SmartConventionInjection
    {
        protected override bool Match(SmartConventionInfo c)
        {
            return c.SourceProp.Name == c.TargetProp.Name;
        }

        protected override void ExecuteMatch(SmartMatchInfo mi)
        {
            var sourceVal = GetValue(mi.SourceProp, mi.Source);
            if (sourceVal == null) return;

            //for value types and string just return the value as is
            if (mi.SourceProp.PropertyType.IsValueType || mi.SourceProp.PropertyType == typeof(string))
            {
                SetValue(mi.TargetProp, mi.Target, sourceVal);
                return;
            }

            //handle arrays
            if (mi.SourceProp.PropertyType.IsArray)
            {
                var arr = sourceVal as Array;
                var arrayClone = arr.Clone() as Array;

                for (var index = 0; index < arr.Length; index++)
                {
                    var arriVal = arr.GetValue(index);
                    if (arriVal.GetType().IsValueType || arriVal.GetType() == typeof(string)) continue;
                    arrayClone.SetValue(Activator.CreateInstance(arriVal.GetType()).InjectFrom<DeepCloneInjection>(arriVal), index);
                }
                SetValue(mi.TargetProp, mi.Target, arrayClone);
                return;
            }

            if (mi.SourceProp.PropertyType.IsGenericType)
            {
                //handle IEnumerable<> also ICollection<> IList<> List<>
                if (mi.SourceProp.PropertyType.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
                {
                    var genericArgument = mi.TargetProp.PropertyType.GetGenericArguments()[0];

                    var tlist = typeof(List<>).MakeGenericType(genericArgument);

                    var list = Activator.CreateInstance(tlist);

                    if (genericArgument.IsValueType || genericArgument == typeof(string))
                    {
                        var addRange = tlist.GetMethod("AddRange");
                        addRange.Invoke(list, new[] { sourceVal });
                    }
                    else
                    {
                        var addMethod = tlist.GetMethod("Add");
                        foreach (var o in sourceVal as IEnumerable)
                        {
                            addMethod.Invoke(list, new[] { Activator.CreateInstance(genericArgument).InjectFrom<DeepCloneInjection>(o) });
                        }
                    }
                    SetValue(mi.TargetProp, mi.Target, list);
                    return;
                }

                throw new NotImplementedException(string.Format("deep clonning for generic type {0} is not implemented", mi.SourceProp.Name));
            }

            //for simple object types create a new instace and apply the clone injection on it
            SetValue(mi.TargetProp, mi.Target, Activator.CreateInstance(mi.TargetProp.PropertyType).InjectFrom<DeepCloneInjection>(sourceVal));
        }
    }

FastDeepCloneInjection (using FastMember):
    public class FastDeepCloneInjection : DeepCloneInjection
    {
        protected override void SetValue(PropertyDescriptor prop, object component, object value)
        {
            var a = TypeAccessor.Create(component.GetType());
            a[component, prop.Name] = value;
        }

        protected override object GetValue(PropertyDescriptor prop, object component)
        {
            var a = TypeAccessor.Create(component.GetType(), true);
            return a[component, prop.Name];
        }
    }

the test:
    class Program
    {
        static void Main()
        {
            const int Iterations = 10000;
            var watch = new Stopwatch();

            var foo = GetFoo();

            CheckDeepCloning();

            Console.WriteLine("start test, iterations = " + Iterations);

            watch.Restart();
            for (int i = 0; i < Iterations; i++)
            {
                new Foo().InjectFrom<DeepCloneInjection>(foo);
            }
            watch.Stop();
            Console.WriteLine("DeepCloning: " + watch.Elapsed);
            watch.Reset();

            watch.Restart();
            for (int i = 0; i < Iterations; i++)
            {
                new Foo().InjectFrom<FastDeepCloneInjection>(foo);
            }
            watch.Stop();
            Console.WriteLine("Faster DeepCloning with FastMember: " + watch.Elapsed);
            watch.Reset();


            watch.Restart();
            for (int i = 0; i < Iterations; i++)
            {
                new Foo().InjectFrom<OldDeepCloneInjection>(foo);
            }
            watch.Stop();
            Console.WriteLine("Old deep clone injection: " + watch.Elapsed);
            watch.Reset();
        }

        private static Foo GetFoo()
        {
            var o = new Foo
            {
                Name = "foo",
                Int32 = 12,
                Int64 = 123123,
                NullInt = 16,
                DateTime = DateTime.Now,
                Doublen = 2312112,
                Foo1 = new Foo { Name = "foo one" },
                Foos = new List<Foo>
                                       {
                                           new Foo {Name = "j1", Int64 = 123, NullInt = 321},
                                           new Foo {Name = "j2", Int32 = 12345, NullInt = 54321},
                                           new Foo {Name = "j3", Int32 = 12345, NullInt = 54321},
                                       },
                FooArr = new[]
                                         {
                                             new Foo {Name = "a1"},
                                             new Foo {Name = "a2"},
                                             new Foo {Name = "a3"},
                                         },
                IntArr = new[] { 1, 2, 3, 4, 5 },
                Ints = new[] { 7, 8, 9 },
            };

            return o;
        }

        private static void CheckDeepCloning()
        {
            var o = GetFoo();

            var c = new Foo().InjectFrom<DeepCloneInjection>(o) as Foo;

            Assert.AreEqual(o.Name, c.Name);
            Assert.AreEqual(o.Int32, c.Int32);
            Assert.AreEqual(o.Int64, c.Int64);
            Assert.AreEqual(o.NullInt, c.NullInt);
            Assert.AreEqual(o.IntArr, c.IntArr);
            Assert.AreEqual(o.Ints, c.Ints);
            Assert.AreEqual(o.DateTime, o.DateTime);

            Assert.AreNotEqual(o.Foo1, c.Foo1);
            Assert.AreNotEqual(o.Foos, c.Foos);
            Assert.AreNotEqual(o.FooArr, c.FooArr);

            //Foo F1
            Assert.AreEqual(o.Foo1.Name, c.Foo1.Name);

            //Foo[] FooArr
            Assert.AreEqual(o.FooArr.Length, c.FooArr.Length);
            Assert.AreNotEqual(o.FooArr[0], c.FooArr[0]);
            Assert.AreEqual(o.FooArr[0].Name, c.FooArr[0].Name);

            //IEnumerable<Foo> Foos
            Assert.AreEqual(o.Foos.Count(), c.Foos.Count());
            Assert.AreNotEqual(o.Foos.First(), c.Foos.First());
            Assert.AreEqual(o.Foos.First().Name, c.Foos.First().Name);
        }
    }

    public class Foo
    {
        public string Name { get; set; }

        public int Int32 { get; set; }

        public long Int64 { set; get; }

        public int? NullInt { get; set; }

        public float Floatn { get; set; }

        public double Doublen { get; set; }

        public DateTime DateTime { get; set; }

        public Foo Foo1 { get; set; }

        public IEnumerable<Foo> Foos { get; set; }

        public Foo[] FooArr { get; set; }

        public int[] IntArr { get; set; }

        public IEnumerable<int> Ints { get; set; }
    }

Last edited Mar 11, 2013 at 11:33 PM by o, version 3

Comments

No comments yet.