2009-02-10 9 views
16

Supongamos que tengo dos clases:¿Cómo copiar el valor de la clase X a la clase Y con el mismo nombre de propiedad en C#?

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

me gustaría copiar el valor de la clase de estudiante a la clase StudentDTO:

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

¿Cómo puedo hacer que por la reflexión u otra solución?

+0

Eche un vistazo a [Automapper] (http://automapper.org/). Esa herramienta fue diseñada para manejar este escenario exacto. – Steven

Respuesta

17

Las listas lo dificultan ... mi respuesta anterior (a continuación) solo se aplica a las propiedades similares (no a las listas). Sospecho que sólo podría tener que escribir y mantener código:

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

edición; solo se aplica a copias poco profundas - no a listas

La reflexión es una opción, pero lenta. En 3.5 puedes construir esto en un código compilado con Expression. Jon Skeet tiene una muestra de pre-laminado de esto en MiscUtil - sólo tiene que utilizar como:

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

Debido a que este utiliza un compilado Expression será enormemente superar el rendimiento de reflexión.

Si no tiene 3.5, utilice reflection o ComponentModel. Si utiliza ComponentModel, al menos puede utilizar HyperDescriptor conseguirlo casi tan rápido como 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)); 
} 
+0

¡eso fue rápido! :) –

+0

muy rápido ... Estaba escribiendo y me dieron un mal mensaje de que hay una nueva respuesta y es ... Así que aborto :( –

+0

¿Tendría algún problema con la lista CourseDTO? Porque CourseDTO puede ser algo diferente a Curso – RvdK

4

Escribe un operador implícito en la clase cualquiera

public static implicit operator StudentDTO(Student student) 
    { 

     //use skeet's library 

     return PropertyCopy<StudentDTO>.CopyFrom(student); 

    } 

Ahora usted puede hacer que

StudentDTO studentDTO = student; 
10

Ok, acabo de buscar el MiscUtil que Marc publicó y es simplemente increíble. Espero que a Mark no le importe agregar el código aquí.

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

} 
4

FYI

Cuando yo estaba teniendo la misma pregunta que me encontré AutoMapper (http://automapper.codeplex.com/) A continuación, después de leer la respuesta de AboutDev que estaba hecho alguna prueba simple, los resultados bastante impresionantes

aquí los resultados de las pruebas:

Auto Test Mapper: 22322 ms

prueba operador implícito: 310 ms

Copia de prueba del inmueble: 250 ms

prueba Emitir Mapper: 281 ms

Y me gustaría subrayar que es solo un ejemplo con las clases (StudentDTO, estudiante), que sólo tienen un par de propiedades, pero ¿Qué pasaría si las clases tuvieran 50 - 100 propiedades? Supongo que afectará el rendimiento dramáticamente.

Más detalles aquí las pruebas: Object copy approaches in .net: Auto Mapper, Emit Mapper, Implicit Operation, Property Copy

+1

¿Cómo compilaste esto? –

0

Hay una biblioteca para hacer precisamente eso - http://emitmapper.codeplex.com/

Es mucho más rápido que AutoMapper, utiliza System.Reflection.Emit, por lo que el código se ejecuta casi tan rápido como si fue escrito a mano.

Cuestiones relacionadas