2009-02-10 4 views
16

Angenommen, ich habe zwei Klassen:Wie kopiert man den Wert von Klasse X in Klasse Y mit demselben Eigenschaftsnamen in 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;} 
} 

Ich mag würde Wert von Student-Klasse zu StudentDTO Klasse kopieren:

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

Wie kann ich tun, dass durch Reflexion oder andere Lösung?

+0

Werfen Sie einen Blick auf [Automapper] (http://automapper.org/). Dieses Tool wurde entwickelt, um dieses genaue Szenario zu behandeln. – Steven

Antwort

17

Die Listen machen es schwierig ... meine frühere Antwort (unten) gilt nur für Like-for-Like-Eigenschaften (nicht die Listen). Ich vermute, Sie haben könnten einfach und Code zu pflegen schreiben:

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

bearbeiten; gilt nur für flach Kopien - keine Listen

Reflexion ist eine Option, aber langsam. In 3.5 können Sie dies in ein kompiliertes Bit des Codes mit Expression einbauen. Jon Skeet hat eine vorgewalzte Probe dieses in MiscUtil - nur verwenden, wie:

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

Da diese verwendet eine kompilierte Expression es wird in beträchtlichem Ausmaß aus-führen Reflexion.

Wenn Sie nicht über 3,5 verfügen, verwenden Sie reflection oder ComponentModel. Wenn Sie Componentmodel verwenden, können Sie zumindest HyperDescriptor verwenden es fast so schnell zu bekommen, wie 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

das war schnell! :) –

+0

sehr schnell ... Ich habe gerade getippt und bekam eine schlechte Nachricht, dass es neue Antwort gibt und es ... Also ich abbrechen :( –

+0

Hätten Sie irgendwelche Probleme mit der CourseDTO Liste? Weil CourseDTO etwas anderes sein kann als Kurs – RvdK

4

einen impliziten Operator in jeder Klasse schreiben

public static implicit operator StudentDTO(Student student) 
    { 

     //use skeet's library 

     return PropertyCopy<StudentDTO>.CopyFrom(student); 

    } 

jetzt können Sie tun, dass

StudentDTO studentDTO = student; 
10

Ok Ich habe gerade die MiscUtil nachgeschlagen, die Marc gepostet hat und es ist einfach großartig. Ich hoffe, Mark hat nichts dagegen, dass ich den Code hier hinzufüge.

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

Als ich die gleiche Frage, die ich AutoMapper (http://automapper.codeplex.com/) Dann nach dem Lesen AboutDev Antwort fand ich war ein paar einfache Tests durchgeführt, die Ergebnisse ziemlich beeindruckend

hier die Testergebnisse:

Test-Auto-Mapper: 22322 ms

-Test Implizite Betreiber: 310 ms

-Test Property Copy: 250 ms

Test-Emit Mapper: 281 ms

Und ich möchte betonen, dass es Probe ist nur mit Klassen (StudentDTO, Studenten), die nur ein paar Eigenschaften haben, aber Was wäre passiert, wenn Klassen 50 - 100 Eigenschaften hätten, würde das die Performance dramatisch beeinflussen.

Weitere Tests Details hier: Object copy approaches in .net: Auto Mapper, Emit Mapper, Implicit Operation, Property Copy

+1

Wie hast du das kompiliert? –

0

Es gibt eine Bibliothek ist nur dafür, dass - http://emitmapper.codeplex.com/

Es ist viel schneller als AutoMapper, verwendet es System.Reflection.Emit, so dass der Code läuft fast so schnell wie wenn es handgeschrieben war.