如何从类X复制到类Y具有相同的属性名称在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;} } 

我想将学生类的值复制到StudentDTO类:

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

我如何通过反思或其他解决scheme来做到这一点?

这些清单使我感到棘手…我早些时候的答复(下文)只适用于类似于财产(而不是清单)。 我怀疑你可能只需要编写和维护代码:

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

编辑; 只适用于拷贝 – 不是列表

反思是一种select,但是很慢。 在3.5中,你可以使用Expression将它编译成一段代码。 Jon Skeet在MiscUtil中有一个预先滚动的样例 – 只是用作:

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

因为这使用了一个编译的Expression ,它将大大超出reflection。

如果你没有3.5,那么使用reflection或ComponentModel。 如果使用ComponentModel,至less可以使用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)); } 

好吧,我只是看了马克张贴的MiscUtil ,它的真棒。 我希望mark不介意在这里添加代码。

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

在任何一个class级写一个隐式的操作符

  public static implicit operator StudentDTO(Student student) { //use skeet's library return PropertyCopy<StudentDTO>.CopyFrom(student); } 

现在你可以做到这一点

 StudentDTO studentDTO = student; 

FYI

当我有同样的问题,我发现AutoMapper( http://automapper.codeplex.com/ )然后在阅读AboutDev的答案我做了一些简单的testing,结果相当令人印象深刻

这里的testing结果:

testing自动映射器:22322毫秒

testing隐式运算符:310毫秒

testing属性复制:250毫秒

testing发射映射器:281毫秒

而且我想强调的是,它只是带有只有几个属性的类(StudentDTO,Student)的样本,但是如果类将有50-100个属性会发生什么,我想这会对性能产生显着影响。

这里有更多的testing细节: .net中的对象复制方法:自动映射器,发射映射器,隐式操作,属性复制

有一个图书馆可以做到这一点 – http://emitmapper.codeplex.com/

它比AutoMapper快得多,它使用System.Reflection.Emit,所以代码几乎和手写一样快。