views:

857

answers:

5

Suppose I have two classes:

public class Student
{
    public int Id {get; set;}
    public string Name {get; set;}
    public IList<Course> Courses{ get; set;}
}

public class StudentDTO
{
    public int Id {get; set;}
    public string Name {get; set;}
    public IList<CourseDTO> Courses{ get; set;}
}

I would like to copy value from Student class to StudentDTO class:

var student = new Student();
StudentDTO studentDTO = student;

How can I do that by reflection or other solution?

+8  A: 

The lists make it tricky... my earlier reply (below) only applies to like-for-like properties (not the lists). I suspect you might just have to write and maintain code:

    Student foo = new Student {
        Id = 1,
        Name = "a",
        Courses = {
            new Course { Key = 2},
            new Course { Key = 3},
        }
    };
    StudentDTO dto = new StudentDTO {
        Id = foo.Id,
        Name = foo.Name,
    };
    foreach (var course in foo.Courses) {
        dto.Courses.Add(new CourseDTO {
            Key = course.Key
        });
    }


edit; only applies to shallow copies - not lists

Reflection is an option, but slow. In 3.5 you can build this into a compiled bit of code with Expression. Jon Skeet has a pre-rolled sample of this in MiscUtil - just use as:

Student source = ...
StudentDTO item = PropertyCopy<StudentDTO>.CopyFrom(student);

Because this uses a compiled Expression it will vastly out-perform reflection.

If you don't have 3.5, then use reflection or ComponentModel. If you use ComponentModel, you can at least use HyperDescriptor to get it nearly as quick as Expression

Student source = ...
StudentDTO item = new StudentDTO();
PropertyDescriptorCollection
     sourceProps = TypeDescriptor.GetProperties(student),
     destProps = TypeDescriptor.GetProperties(item),
foreach(PropertyDescriptor prop in sourceProps) {
    PropertyDescriptor destProp = destProps[prop.Name];
    if(destProp != null) destProp.SetValue(item, prop.GetValue(student));
}
Marc Gravell
that was quick! :)
Ric Tokyo
very quick... I was just typing and got a bad message that there is new answer and its ... So I abort :(
Sachin
Would you have any problems with the CourseDTO list? because CourseDTO can be something different than Course
PoweRoy
Ah - yes, the list is a problem... hang on...
Marc Gravell
Marc, awesome link to MiscUtil. That code is so elegant. I hope you don't mind me posting it here because people really should see it. I'm modding your answer up man! That link was a great find.
AboutDev
Oh, MiscUtil doesn't help for my CourseDTO list. Other any solution? Because my class has a lot of properties and child list.
Samnang
It would perhaps be possible to get the expression to invoke the same expression approach recursively for the children... the problem is that there would need to be some "fluent" wrappers along the way due to limitations inside `Expression` in 3.5 (fixed in 4.0).
Marc Gravell
Unfortunately, I don't have any time to investigate re-writing it this morning; suggest you use the helper approach for the children. and copy the parent manually?
Marc Gravell
+4  A: 

Write a implicit operator in anyone class

    public static implicit operator StudentDTO(Student student)
    {

        //use skeet's library

        return PropertyCopy<StudentDTO>.CopyFrom(student);

    }

now you can do that

StudentDTO studentDTO = student;
Kuldip Saini
+5  A: 

Ok I just looked up the MiscUtil that Marc posted about and its just awesome. I hope mark doesn't mind me adding the code here.

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel;
using System.Linq.Expressions;

namespace ConsoleApplication1
{
    class Program
    {
        public class Student
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public IList<int> Courses { get; set; }
            public static implicit operator Student(StudentDTO studentDTO)
            {
                return PropertyCopy<Student>.CopyFrom(studentDTO);
            }
        }

        public class StudentDTO
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public IList<int> Courses { get; set; }
            public static implicit operator StudentDTO(Student student)
            {
                return PropertyCopy<StudentDTO>.CopyFrom(student);
            }
        }


        static void Main(string[] args)
        {
            Student _student = new Student();
            _student.Id = 1;
            _student.Name = "Timmmmmmmmaaaahhhh";
            _student.Courses = new List<int>();
            _student.Courses.Add(101);
            _student.Courses.Add(121);

            StudentDTO itemT = _student;

            Console.WriteLine(itemT.Id);
            Console.WriteLine(itemT.Name);
            Console.WriteLine(itemT.Courses.Count);
        }


    }


    // COOLEST PIECE OF CODE FROM - http://www.yoda.arachsys.com/csharp/miscutil/

    /// <summary>
    /// Generic class which copies to its target type from a source
    /// type specified in the Copy method. The types are specified
    /// separately to take advantage of type inference on generic
    /// method arguments.
    /// </summary>
    public class PropertyCopy<TTarget> where TTarget : class, new()
    {
        /// <summary>
        /// Copies all readable properties from the source to a new instance
        /// of TTarget.
        /// </summary>
        public static TTarget CopyFrom<TSource>(TSource source) where TSource : class
        {
            return PropertyCopier<TSource>.Copy(source);
        }

        /// <summary>
        /// Static class to efficiently store the compiled delegate which can
        /// do the copying. We need a bit of work to ensure that exceptions are
        /// appropriately propagated, as the exception is generated at type initialization
        /// time, but we wish it to be thrown as an ArgumentException.
        /// </summary>
        private static class PropertyCopier<TSource> where TSource : class
        {
            private static readonly Func<TSource, TTarget> copier;
            private static readonly Exception initializationException;

            internal static TTarget Copy(TSource source)
            {
                if (initializationException != null)
                {
                    throw initializationException;
                }
                if (source == null)
                {
                    throw new ArgumentNullException("source");
                }
                return copier(source);
            }

            static PropertyCopier()
            {
                try
                {
                    copier = BuildCopier();
                    initializationException = null;
                }
                catch (Exception e)
                {
                    copier = null;
                    initializationException = e;
                }
            }

            private static Func<TSource, TTarget> BuildCopier()
            {
                ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
                var bindings = new List<MemberBinding>();
                foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties())
                {
                    if (!sourceProperty.CanRead)
                    {
                        continue;
                    }
                    PropertyInfo targetProperty = typeof(TTarget).GetProperty(sourceProperty.Name);
                    if (targetProperty == null)
                    {
                        throw new ArgumentException("Property " + sourceProperty.Name + " is not present and accessible in " + typeof(TTarget).FullName);
                    }
                    if (!targetProperty.CanWrite)
                    {
                        throw new ArgumentException("Property " + sourceProperty.Name + " is not writable in " + typeof(TTarget).FullName);
                    }
                    if (!targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                    {
                        throw new ArgumentException("Property " + sourceProperty.Name + " has an incompatible type in " + typeof(TTarget).FullName);
                    }
                    bindings.Add(Expression.Bind(targetProperty, Expression.Property(sourceParameter, sourceProperty)));
                }
                Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings);
                return Expression.Lambda<Func<TSource,TTarget>>(initializer, sourceParameter).Compile();
            }
        }
    }

}
AboutDev
+3  A: 

FYI

When I was having the same question I found AutoMapper (http://automapper.codeplex.com/) Then after reading AboutDev's answer i was done some simple test, the results pretty impressive

here the test results:

Test Auto Mapper:22322 ms

Test Implicit Operator:310 ms

Test Property Copy:250 ms

Test Emit Mapper:281 ms

And i would like to underline that it is sample only with classes(StudentDTO, Student) which have only a couple of properties, but what would happened if classes would have 50 - 100 properties, i guess it will affect performance dramatically.

More tests details here: http://stackoverflow.com/questions/3457657/object-copy-approaches-in-net-auto-mapper-emit-mapper-implicit-operation-pro

bigb
How did you compile this?
Chris
A: 

There's a library for doing just that - http://emitmapper.codeplex.com/

It's much faster than AutoMapper, it uses System.Reflection.Emit, so the code runs almost as fast as if it was hand-written.

arconaut