Как скопировать значение из класса X в класс Y с тем же именем свойства в C #? - PullRequest
16 голосов
/ 10 февраля 2009

Предположим, у меня есть два класса:

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;}
}

Я хотел бы скопировать значение из класса Student в класс StudentDTO:

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

Как я могу сделать это с помощью отражения или другого решения?

Ответы [ 5 ]

17 голосов
/ 10 февраля 2009

Списки усложняют ... Мой предыдущий ответ (ниже) относится только к подобным свойствам (но не к спискам). Я подозреваю, что вам, возможно, просто нужно написать и поддерживать код:

    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
        });
    }

редактировать; применяется только к мелким копиям - не спискам

Отражение - вариант, но медленный. В 3.5 вы можете встроить это в скомпилированный фрагмент кода с Expression. У Джона Скита есть предварительно свернутый образец этого в MiscUtil - просто используйте как:

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

Поскольку он использует скомпилированный Expression, он значительно превзойдёт отражение.

Если у вас нет 3.5, используйте отражение или ComponentModel. Если вы используете ComponentModel, вы можете по крайней мере использовать HyperDescriptor, чтобы получить почти так быстро, как 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));
}
10 голосов
/ 10 февраля 2009

Хорошо, я только что посмотрел MiscUtil , о котором писал Марк, и это просто потрясающе. Надеюсь, Марк не возражает, если я добавлю здесь код.

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();
            }
        }
    }

}
5 голосов
/ 09 августа 2010

FYI

Когда у меня возник тот же вопрос, я обнаружил AutoMapper (http://automapper.codeplex.com/) Затем, прочитав ответ AboutDev, я провел простой тест, результаты которого довольно впечатляющие

вот результаты теста:

Тест Авто картограф: 22322 мс

Проверка неявного оператора: 310 мс

Проверка свойства копии: 250 мс

Тест Emit Mapper: 281 мс

И я хотел бы подчеркнуть, что это образец только с классами (StudentDTO, Student), которые имеют только несколько свойств, но что произойдет, если у классов будет 50–100 свойств, я думаю, это сильно повлияет на производительность.

Больше подробностей о тестах здесь: Копирование объектов в .net: Auto Mapper, Emit Mapper, неявная операция, копирование свойства

4 голосов
/ 10 февраля 2009

Написать неявный оператор в любом классе

    public static implicit operator StudentDTO(Student student)
    {

        //use skeet's library

        return PropertyCopy<StudentDTO>.CopyFrom(student);

    }

теперь вы можете сделать это

StudentDTO studentDTO = student;
0 голосов
/ 10 августа 2010

Для этого есть библиотека - http://emitmapper.codeplex.com/

Это намного быстрее, чем AutoMapper, он использует System.Reflection.Emit, поэтому код работает почти так же быстро, как если бы он был написан от руки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...