2009-02-10 6 views
16

Supposons que j'ai deux classes:Comment copier la valeur de la classe X vers la classe Y avec le même nom de propriété dans 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;} 
} 

Je voudrais copier la valeur de la classe des élèves en classe StudentDTO:

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

Comment puis-je faire par la réflexion ou toute autre solution?

+0

Jetez un oeil à [Automapper] (http://automapper.org/). Cet outil a été conçu pour gérer ce scénario exact. – Steven

Répondre

17

Les listes rendent difficile ... ma réponse précédente (ci-dessous) ne s'applique qu'aux propriétés similaires (pas aux listes). Je suppose que vous pourriez avoir juste besoin d'écrire et de maintenir le 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 
     }); 
    } 

modifier; s'applique uniquement à peu profond copies - listes non

La réflexion est une option, mais lente. En 3.5, vous pouvez construire ceci dans un bit de code compilé avec Expression. Jon Skeet a un échantillon pré-roulé de cela dans MiscUtil - il suffit d'utiliser comme:

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

Parce que celui-ci utilise un langage compilé Expression il surclasser largement la réflexion.

Si vous n'avez pas 3.5, utilisez la réflexion ou ComponentModel. Si vous utilisez ComponentModel, vous pouvez au moins utiliser HyperDescriptor pour l'obtenir près aussi rapide que 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

c'était rapide! :) –

+0

très rapide ... Je tapais juste et ai reçu un mauvais message qu'il y a une nouvelle réponse et son ... Donc j'abandonne :( –

+0

Auriez-vous des problèmes avec la liste CourseDTO? Parce que CourseDTO peut être quelque chose de différent Course – RvdK

4

Ecrire un opérateur implicite en classe quelqu'un

public static implicit operator StudentDTO(Student student) 
    { 

     //use skeet's library 

     return PropertyCopy<StudentDTO>.CopyFrom(student); 

    } 

maintenant vous pouvez le faire

StudentDTO studentDTO = student; 
10

Ok, je viens de chercher le MiscUtil que Marc a publié à propos de et c'est tout simplement génial. J'espère que Mark ne me dérange pas d'ajouter le code ici.

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

Pour votre information

Quand je faisais la même question que j'ai trouvé AutoMapper (http://automapper.codeplex.com/) Puis après avoir lu la réponse de AboutDev j'ai fait quelques test simple, les résultats assez impressionnants

ici les résultats des tests:

Auto test Mapper: 22322 ms

test Opérateur implicite: 310 ms

Test Property Copy: 250 ms

test Emit Mapper: 281 ms

Et je voudrais souligner qu'il est échantillon uniquement avec des classes (StudentDTO, étudiants) qui ont seulement quelques propriétés, mais que se passerait-il si les classes avaient 50 à 100 propriétés, je suppose que cela affectera considérablement la performance.

essais Plus de détails ici: Object copy approaches in .net: Auto Mapper, Emit Mapper, Implicit Operation, Property Copy

+1

Comment l'avez-vous compilé? –

0

Il y a une bibliothèque pour faire juste que - http://emitmapper.codeplex.com/

Il est beaucoup plus rapide que AutoMapper, il utilise System.Reflection.Emit, de sorte que le code fonctionne presque aussi vite que si c'était écrit à la main.

Questions connexes