假设我有两个类:
public class Student { public int Id {get; set;} public string Name {get; set;} public IListCourses{ get; set;} } public class StudentDTO { public int Id {get; set;} public string Name {get; set;} public IList Courses{ get; set;} }
我想将Student类中的值复制到StudentDTO类:
var student = new Student(); StudentDTO studentDTO = student;
我怎么能通过反思或其他解决方案来做到这一点?
列表使它变得棘手...我之前的回复(下面)仅适用于类似的属性(不是列表).我怀疑你可能只需要编写和维护代码:
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
.Jon Skeet在MiscUtil中有一个预先推出的样本- 只需用作:
Student source = ... StudentDTO item = PropertyCopy.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)); }
好的,我只是查看了Marc发布的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 IListCourses { get; set; } public static implicit operator Student(StudentDTO studentDTO) { return PropertyCopy .CopyFrom(studentDTO); } } public class StudentDTO { public int Id { get; set; } public string Name { get; set; } public IList Courses { get; set; } public static implicit operator StudentDTO(Student student) { return PropertyCopy .CopyFrom(student); } } static void Main(string[] args) { Student _student = new Student(); _student.Id = 1; _student.Name = "Timmmmmmmmaaaahhhh"; _student.Courses = new List (); _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/ /// /// 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. /// public class PropertyCopywhere TTarget : class, new() { /// /// Copies all readable properties from the source to a new instance /// of TTarget. /// public static TTarget CopyFrom(TSource source) where TSource : class { return PropertyCopier .Copy(source); } /// /// 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. /// private static class PropertyCopierwhere TSource : class { private static readonly Func 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 BuildCopier() { ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source"); var bindings = new List (); 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 >(initializer, sourceParameter).Compile(); } } } }
FYI
当我遇到同样的问题时,我发现了AutoMapper(http://automapper.codeplex.com/)然后在阅读了AboutDev的答案后,我做了一些简单的测试,结果令人印象深刻
这里的测试结果如下:
测试自动映射器:22322毫秒
测试隐式算子:310毫秒
测试属性副本:250毫秒
测试发射映射器:281毫秒
我想强调它只是样本(StudentDTO,Student),它只有几个属性,但如果类有50-100个属性会发生什么,我猜它会显着影响性能.
此处有更多测试详细信息: .net中的对象复制方法:自动映射器,发射映射器,隐式操作,属性复制