C# – 将属性值从一个实例复制到另一个实例,不同的类

我有两个C#类,它们具有许多相同的属性(按名称和类型)。 我希望能够将Defect实例中的所有非空值复制到DefectViewModel的实例中。 我希望用reflection来做,使用GetType().GetProperties() 。 我尝试了以下方法:

 var defect = new Defect(); var defectViewModel = new DefectViewModel(); PropertyInfo[] defectProperties = defect.GetType().GetProperties(); IEnumerable viewModelPropertyNames = defectViewModel.GetType().GetProperties().Select(property => property.Name); IEnumerable propertiesToCopy = defectProperties.Where(defectProperty => viewModelPropertyNames.Contains(defectProperty.Name) ); foreach (PropertyInfo defectProperty in propertiesToCopy) { var defectValue = defectProperty.GetValue(defect, null) as string; if (null == defectValue) { continue; } // "System.Reflection.TargetException: Object does not match target type": defectProperty.SetValue(viewModel, defectValue, null); } 

最好的方法是什么? 我应该维护单独的Defect属性列表和DefectViewModel属性,以便我可以执行viewModelProperty.SetValue(viewModel, defectValue, null)吗?

编辑:多亏了Jordão和Dave的答案,我选择了AutoMapper。 DefectViewModel位于WPF应用程序中,因此我添加了以下App构造函数:

 public App() { Mapper.CreateMap() .ForMember("PropertyOnlyInViewModel", options => options.Ignore()) .ForMember("AnotherPropertyOnlyInViewModel", options => options.Ignore()) .ForAllMembers(memberConfigExpr => memberConfigExpr.Condition(resContext => resContext.SourceType.Equals(typeof(string)) && !resContext.IsSourceValueNull ) ); } 

然后,我只是拥有以下行,而不是所有PropertyInfo业务:

 var defect = new Defect(); var defectViewModel = new DefectViewModel(); Mapper.Map(defect, defectViewModel); 

看看AutoMapper 。

用这个替换你的错误行:

 PropertyInfo targetProperty = defectViewModel.GetType().GetProperty(defectProperty.Name); targetProperty.SetValue(viewModel, defectValue, null); 

您发布的代码正在尝试在DefectViewModel对象上设置Defect -tied属性。

在组织代码方面,如果你不想要像AutoMapper这样的外部库,你可以使用类似mixin的方案将代码分开,如下所示:

 class Program { static void Main(string[] args) { var d = new Defect() { Category = "bug", Status = "open" }; var m = new DefectViewModel(); m.CopyPropertiesFrom(d); Console.WriteLine("{0}, {1}", m.Category, m.Status); } } // compositions class Defect : MPropertyGettable { public string Category { get; set; } public string Status { get; set; } // ... } class DefectViewModel : MPropertySettable { public string Category { get; set; } public string Status { get; set; } // ... } // quasi-mixins public interface MPropertyEnumerable { } public static class PropertyEnumerable { public static IEnumerable GetProperties(this MPropertyEnumerable self) { return self.GetType().GetProperties().Select(property => property.Name); } } public interface MPropertyGettable : MPropertyEnumerable { } public static class PropertyGettable { public static object GetValue(this MPropertyGettable self, string name) { return self.GetType().GetProperty(name).GetValue(self, null); } } public interface MPropertySettable : MPropertyEnumerable { } public static class PropertySettable { public static void SetValue(this MPropertySettable self, string name, T value) { self.GetType().GetProperty(name).SetValue(self, value, null); } public static void CopyPropertiesFrom(this MPropertySettable self, MPropertyGettable other) { self.GetProperties().Intersect(other.GetProperties()).ToList().ForEach( property => self.SetValue(property, other.GetValue(property))); } } 

这样,实现属性复制的所有代码都与使用它的类分开。 您只需要在其接口列表中引用mixins。

请注意,这不像AutoMapper那样强大或灵活,因为您可能希望复制具有不同名称或属性的某些子集的属性。 如果属性不提供必要的getter或setter或它们的类型不同,它可能会彻底失败。 但是,它仍然可能足以满足您的目的。

这很便宜而且容易。 它使用System.Web.Script.Serialization和一些扩展方法以方便使用:

 public static class JSONExts { public static string ToJSON(this object o) { var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); return oSerializer.Serialize(o); } public static List FromJSONToListOf(this string jsonString) { var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); return oSerializer.Deserialize>(jsonString); } public static T FromJSONTo(this string jsonString) { var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); return oSerializer.Deserialize(jsonString); } public static T1 ConvertViaJSON(this object o) { return o.ToJSON().FromJSONTo(); } } 

这里有一些类似但不同的类:

 public class Member { public string Name { get; set; } public int Age { get; set; } public bool IsCitizen { get; set; } public DateTime? Birthday { get; set; } public string PetName { get; set; } public int PetAge { get; set; } public bool IsUgly { get; set; } } public class MemberV2 { public string Name { get; set; } public int Age { get; set; } public bool IsCitizen { get; set; } public DateTime? Birthday { get; set; } public string ChildName { get; set; } public int ChildAge { get; set; } public bool IsCute { get; set; } } 

以下是实施方法:

 var memberClass1Obj = new Member { Name = "Steve Smith", Age = 25, IsCitizen = true, Birthday = DateTime.Now.AddYears(-30), PetName = "Rosco", PetAge = 4, IsUgly = true, }; string br = "

"; Response.Write(memberClass1Obj.ToJSON() + br); // just to show the JSON var memberClass2Obj = memberClass1Obj.ConvertViaJSON(); Response.Write(memberClass2Obj.ToJSON()); // valid fields are filled

首先,我不会将该代码(某处)放在外部但是在ViewModel的构造函数中:

 class DefectViewModel { public DefectViewModel(Defect source) { ... } } 

如果这是唯一的类(或少数几个),我不会进一步自动化,但写出属性分配。 自动化它看起来不错,但可能会有比您预期的更多exception和特殊情况。

你有可能让两个类都实现一个定义共享属性的接口吗?